泛型定义
- Java 1.5 新特性,本质就是参数化类型,即所操作的数据类型被指定为一个参数
- 泛型的好处是在编译的时候进行类型安全检查,并且所有的强制转换都是自动和隐式的,以提高代码的重用率
主要规则
- 只能是 类 类型,不能是基础简单类型(Integer / int )
- 同一种泛型允许有多个实现实例,但实现实例之间互不兼容
- 泛型的参数类型可以有多个
- 泛型的参数类型可以使用extends、super语句,且可以使用通配符类型(?)
泛型类
-
单参数泛型类
-
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ...... }
-
多参数泛型类
-
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { ..... }
泛型接口
public interface List<E> extends Collection<E> {
......
}
泛型方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 在调用泛型方法时,若不指定泛型的情况下,泛型变量的类型为该方法中几种类型的同一父类的最小级,直到 Object
- 初始化泛型类时,若不指定泛型,则默认取 Object
通配符(?)与 T 的区别
- T:作用于模板上,用于将数据类型进行参数化,不能用于实例化对象
- ?:在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义
上界类型通配符(? extends)
List<? extends Number> list = new ArrayList<Integer>();
// 正确
Number numObject = list.get(0);
// 错误 Number无法直接赋予给 Integer(Java语法规范
Integer intObject = list.get(0);
// 错误 list无法确认实例化对象的具体类型(可能是 Integer、Long、Float等),add操作受限
list.add(new Integer(1));
- 上界类型通配符 add 方法受限,但可以获取列表中的各种类型的数据,并赋值给父类型(extends Number)的引用
下界类型通配符(? super )
List<? super Integer> list = new ArrayList<Number>();
// 正确
list.add(new Integer(1));
// 错误 List<? super Integer> 无法确认 list 中存放的对象具体类型,get操作受限
Number numObj = list.get(0);
Integer intObj = list.get(0);
- 下界类型通配符 get 方法受限,但可以往列表中添加各种数据类型的对象,同时限定通配符总是包括自己
原始类型
-
Java 总是会自动的为泛型类型提供一个相应的原始类型
-
所谓原始类型就是是指泛型的第一个限定类型(从左向右)
-
public class ArrayList<E extends Integer&Serializable> { ...... }
- extends 后可以继承多个类、多个接口,原始类型为从左向右第一个类或接口(即这里的 Integer
- 为了提高效率,应该将标签接口(即没有方法的接口)放在边界限定列表的末尾
-
无限定类型泛型的原始类型默认为 Objec
-
public class ArrayList<E> { ...... } 等价于 public class ArrayList<E extends Object> { ...... }
-
类型擦除
-
Java 中泛型的实现原理是类型擦除(type erasure)
-
类型擦除是在编译器进行代码编译这个阶段进行的,在编译的时候泛型的类型参数会被原始类型(raw type)所替代
-
List<String> strList = new ArrayList<String>(); List<Integer> intList = new ArrayList<Integer>(); // true System.out.println(strList.getClass()==intList.getClass());