泛型产生场景
开发一个能够存储各种对象的容器,如果用 Object 进行存储,显示转换的时候可能出现异常。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
定义 List
泛型使用场景
- 泛型类
类后面添加类型参数
1 | public class Box<T> { |
支持多个类型参数
1 | public class MultiGenericContainer<T, S> { |
- 泛型接口
1 | public interface Generator<T> { |
- 泛型方法
方法调用时可以接收不同类型的参数,编译期根据不同的参数类型进行对应处理
1 | public static < E > void printArray( E[] inputArray ){ |
泛型通配符
限定通配符:
- E:元素
- K:键
- N:数字
- T:类型
- V:值
- S、U、V 等:多参数情况中的第 2、3、4 个类型
非限定通配符: ?
某些情况下,编写指定未知类型的代码很有用。问号 (?) 通配符可用于使用泛型代码表示未知类型。通配符可用于参数、字段、局部变量和返回类型。但最好不要在返回类型中使用通配符,因为确切知道方法返回的类型更安全。
泛型通配符使用注意
将指定的泛型类型控制为指定的类型,需要使用上下界限定符 extends 和 super
< T extends UpperBoundType> list:list 中元素类型必须是 UpperBoundType 类或者是 UpperBoundType 的子类
< T super LowerBoundType> list:list 中元素类型必须是 LowerBoundType 或者是 LowerBoundType 的父类
1 | Class Fruit{} |
- Java 并不支持泛型的向上转型,下面写法将不会通过编译
1 | Plate<Fruit> plate = new Plate<Apple>(); //Error |
下面写法可以通过编译,即 plate 可以指向 Fruit 类的对象或者 Fruit 子类对象
1 | Plate<? extends Fruit> plate = new Plate<Apple>(); |
- extends 只能读取对象,不能添加对象
因为 plate 指向 Fruit 类的对象或者 Fruit 子类对象,但是 Fruit 的子类不一定只有 Apple,还有可能是 Orange ,添加的元素不能确定是哪种具体类型。
但是 get 的时候,可以知道 get 到的元素都可以转为 Fruit 类型,所以可以读取对象
1 | Plate<? extends Fruit> plate = new Plate<Apple>(); |
- super 只能添加对象,不能读取对象
plate 指向 Apple 类的对象或者 Apple 父类的对象,Fruit 是 Apple 的父类,所以添加对象是可行的。
但是 get 的时候,不清楚 get 到的元素具体类型,有可能是 Apple 有可能是 Fruit,更有可能是 Fruit 的父类,只能使用 Object 去 get ,才能编译不报错。
1 | Plate<? super Apple> plate = new Plate<Fruit>(); |
泛型擦除
1 | ArrayList<String> a = new ArrayList<String>(); |
泛型只存在于编译阶段,在编译阶段会进行泛型检查,检查元素是否满足类型要求,不满足时编译将不通过;
在编译后的 class 文件中,进行了泛型擦除,c1,c2 都是 ArrayList.class,不存在泛型概念。