泛型的目的
- 通过引入类型参数,使得相同的代码可以被复用;传入不同的类型参数,就可以适用于不同的场景。
- 通过编译器来避免代码中可能存在的错误,在编译阶段排除可能存在的错误。
两种泛型
- 泛型类
- 泛型方法
满足is-a关系的变量都可以作为方法的参数。
但是,A和B之间的关系,以及A的泛型类与B的泛型类之间的关系,是两种不同的关系。
泛型类之间的可以存在继承关系。
类型推断
-
类型推断:泛型方法在调用时,无需指定类型参数,编译器会从调用的实参中推断出这个泛型参数。
-
泛型类和非泛型类中的泛型构造器的类型推导
受限制类型参数
-
上限:T extends UpperBound
限定一个未知的类型为一个指定的类型,或者此类型的子类型。 -
上限通配符
? extends UpperBound
具体的类型未知,但此类型为一个指定的类型,或者此类型的子类型。 -
下限通配符
? super LowerBound
-
未限定通配符
- 通过 ? 符号指定。
- 可以用于参数的类型、域的类型、本地变量的类型,有时也可用做返回值的类型;但是不作为泛型方法调用的参数、泛型类的实例化,以及超类型。
- 使用通配符进行子类化,例如:
List<?>
是List<String>
、List<Integer>
的超类型。
通配符捕获(Wildcard Captrue)
在编译阶段对表达式求值时,编译器可以从代码中推测出一种特定的类型,这种情况被称为通配符捕获。
一般我们不用关注这个过程,除非编译过程中出现通配符捕获相关的错误。
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(2));
}
public class WildcardErrorBad {
void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
Number temp = l1.get(0);
l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
// got a CAP#2 extends Number;
// same bound, but different types
l2.set(0, temp); // expected a CAP#1 extends Number,
// got a Number
}
}
通配符的使用规则
对于通配符的使用来说,一个很让人迷惑的地方在于何时使用extends,何时使用super。总体上看,可以通过把参数分为以下两类来决定使用哪种关键字。
-
入变量
提供数据,供本代码使用。 -
出变量
保存数据,供其它地方的代码使用。
例如,对于Collections
的copy
方法:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
dest是出变量,src是入变量。
基于这种分类,可以约定以下使用规则:
- 入变量,使用
extends
关键字定义变量的上限。 - 出变量,使用
super
关键字定义变量的下限。 - 入变量,准备调用
Object
类的方法,使用?
未受限通配符。 - 同时作为入变量和出变量,不使用通配符。
简单地说,就是入变量只能读,不能写;出变量只能写,不能读。
特别注意:
通过<? extends ...>
定义的变量,可以被认为是一种只读的变量。例如: List<? extends ...>的变量只能执行以下几种操作:
add(null)
- 调用
clear
- 获取iterator,调用
remove
- 捕获通配符,往其中添加从中读出来的元素。
扩展思考:
-
为什么上限通配符定义变量只能读,不能写?
对于使用? extends T定义的变量参数,可以是T的任意子类。
在执行读操作的时候,可以用T来引用读出来的元素,因为T是这些元素类型的父类。
但是,如果执行写操作,编译器不知道写入的是T的哪个子类型。假设A、B均是T的子类,并且B继承A,即T <- A <-- B。编译器无法推断出写入的元素类型是T,还是A,还是B。 -
为什么下限通配符定义的变量只能写,不能读?
对于使用? super T定义的变量参数,可以是T的任意父类。
在执行写操作的时候,无论实际是哪种类型,都是T的父类。
执行读操作的时候,编译器无法推断出读出的元素类型,不知道该用什么类型去引用。
网友评论