Java基础之泛型

泛型

介绍

  • 泛型是JDK 5 中引入的新特性

    • 提供编译时类型安全检测机制
    • 允许程序员在编译时检测到非法类型
  • 本质:参数化类型

    • 所操作的数据类型被指定为一个参数
  • 泛型是一种类似模板代码的技术,不同语言的泛型实现方式不一定相同

    • Java 的泛型实现方式是擦拭法(Type Erasure)

    • 擦拭法指:虚拟机对泛型其实一无所知,所有的工作都是编译器做的

Java泛型

Java 的泛型是由编译器在编译时实行的

  • 编译器内部永远把所有类型 T 视为 Object 处理
    • 但需要转型的时候,编译器会根据 T 的类型自动实行安全地强制转型
  • 局限
    1. <T>不能是基本类型,例如int
      • 因为实际类型是 ObjectObject 类型无法持有基本类型
    2. 无法取得带泛型的 Class
      • 所有泛型实例无论T的类型 getClass()返回同一个Class实例
      • 因为编译后全部都是 ClassName<Object>
    3. 无法判断带泛型的类型
      • 并不存在ClassaName<String>.class,而是只有唯一的ClassName.class
      • x instanceof Pair<String> 不能判断
    4. 不能实例化 T 类型
      • 例如:new T()
  • 子类可以获取父类的泛型类型<T>

通配符

  • 类型通配符一般是使用 ? 代替具体的类型参数
    • 例如:List<?> 在逻辑上是 List<String>List<Integer> 等所有 List<具体类型实参> 的父类
有界类型
  • 限制类型参数的类型种类范围

    • 如:只接受NumberNumber子类的实例;就是有界类型参数的目的
  • 声明一个有界的类型参数

    1. <> 使用通配符 ?
    2. extendssuper 关键字
      • extends 表示该类型的子类型参数
      • supre 表示该类型的父类型参数
    3. 紧跟其上、下界
<? extends T>

<? extends T>:通配符代表的类型是 T 类型的子类

  • 规定上限类型 T
<? super T>
  • <? super T>:通配符代表的类型是 T 类型的父类
    • 规定下限类型 T
示例
public class MaximumTest{
   // 比较三个值并返回最大值
   public static <T extends Comparable<T>> T maximum(T x, T y, T z){    // 定义泛型方法,返回值是 Comparable 类型子类             
      T max = x; 														// 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; 														// y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; 														// 现在 z 更大           
      }
      return max; 														// 返回最大对象
   }
   public static void main( String args[] ){
       System.out.println(maximum( 3, 4, 5 ));							// 5
       System.out.println(maximum( 6.6, 8.8, 7.7 ));					// 8.8
       System.out.println(maximum( "pear", "apple", "orange" ));		// pear
       // 在传入参数后 int、double类型数据自动装箱为包装类型;Integer、Double、String 类型都实现了 Comparable 接口
   }
}

泛型方法

  • 调用时可接收不同类型参数

    • 根据传递给泛型方法的参数类型,编译器适当的处理每一个方法调用
    • 要防止重复定义方法
      • 例如:public boolean equals(T obj)
      • 错误重写 equals 方法
  • 声明

    • 类型参数部分,使用尖括号分隔,在返回类型值之前
      • 例如:<E>
    • 包含一个或多个类型参数,用逗号隔开
      • 一个泛型参数被称为一个类型变量,用于指定一个泛型类型名称的标识符
        • 例如:<E, E>
    • 泛型方法的声明的类型参数只能代表引用类型,不能是原始类型
      • 如:int、double、char 等 需要使用包装类型
  • 类型参数能被用来声明返回值类型

    • 且能作为泛型方法得到的实际参数类型的占位符
  • 类型参数使用大写形式且比较短,一般一个字母

    • Java 库中使用变量 E 表示集合的元素类型

    • KV 表示键与值的类型

      • 比如:Map 的键与值
    • 通常使用 TUS 表示“任意类型”

  • public class Test{
       // 泛型方法 printArray                         
       public static <E> void printArray( E[] inputArray ){
             // 定义泛型 <E>,使 E[] 可以接收任意类型数据定义数组   
             for ( E element : inputArray ){        
                System.out.printf( "%s ", element );  // 遍历输出数组元素 
             } 
        }
     
        public static void main( String args[] ){
            // 创建不同类型数组: Integer, Double 和 Character
            Integer[] intArray = { 1, 2, 3, 4, 5 };
            Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
            Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
     
            printArray(intArray); 				// 传递整型数组:1 2 3 4 5 
     
            printArray( doubleArray ); 			// 传递双精度型数组:1.1 2.2 3.3 4.4 
     
            printArray( charArray ); 			// 传递字符型数组:H E L L O 
        } 
    }
    

泛型类

定义
  • 类名后面添加类型参数声明部分

  • 包含一个或多个类型参数,参数间用逗号隔

  • 泛型参数被称为类型变量,用于指定一个泛型类型名称的标识符

    • 因为接受一或多个参数,这些类被称为参数化的类或参数化的类型
  • 实际数据类型在类被实例化的时候声明

  • public class Box<T> {  											   // 泛型类
    
        private T t;												   // 泛型属性
    
        public void add(T t) {										   // 泛型方法
            this.t = t;
        }
        public T get() {
            return t;
        }
    
        public static void main(String[] args) {
            Box<Integer> integerBox = new Box<Integer>();			   // 实例化对象声明泛型为 Integer 类型
            Box<String> stringBox = new Box<String>();				   // 实例化对象声明泛型为 String 类型
    
            integerBox.add(10);
            stringBox.add("菜鸟教程");
    
            System.out.println("整型值为:" + integerBox.get());			// 整型值为:10
            System.out.println("字符串为:" + stringBox.get());			// 字符串为:菜鸟教程
        }
    }      
    
继承
  • 一个类可以继承自一个泛型类

    • 例如:父类 Pair<Integer>,子类是 IntPair
      • public class IntPair extends Pair<Integer> {}
        • 继承泛型类,已经声明泛型类型
        • 子类IntPair并没有泛型类型,正常使用即可
  • 无法获取 Pair<T>T类型

    • 即给定变量 Pair<Integer> p,无法从 p 中获取到 Integer 类型
  • 但父类是泛型类型的情况下,编译器必须把类型 T保存到子类的 class 文件

    • 不然编译器就不知道 IntPair 只能存取 Integer 类型
      • IntPair TInteger 类型
  • 在继承了泛型类型的情况下,子类可以获取父类的泛型类型

    • 例如:IntPair 可以获取到父类的泛型类型 Integer

    • 获取父类的泛型类型代码比较复杂

    • import java.lang.reflect.ParameterizedType;
      import java.lang.reflect.Type;
      
      public class Main {
          // 获取父类泛型类型
          public static void main(String[] args) {
              Class<IntPair> clazz = IntPair.class;
              Type t = clazz.getGenericSuperclass();
              if (t instanceof ParameterizedType) {
                  ParameterizedType pt = (ParameterizedType) t;
                  Type[] types = pt.getActualTypeArguments(); 			// 可能有多个泛型类型
                  Type firstType = types[0]; 								// 取第一个泛型类型
                  Class<?> typeClass = (Class<?>) firstType;
                  System.out.println(typeClass); 							// Integer
              }
      
          }
      }
      
      class Pair<T> {
          private T first;
          private T last;
          public Pair(T first, T last) {
              this.first = first;
              this.last = last;
          }
          public T getFirst() {
              return first;
          }
          public T getLast() {
              return last;
          }
      }
      
      class IntPair extends Pair<Integer> {
          public IntPair(Integer first, Integer last) {
              super(first, last);
          }
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值