1. 通配符有哪几类
?
表示通配符- Java 中通配符分三类
<?>
: 无限定通配符<? extends T>
: 有限定通配符,匹配T
或T
的某个子类型<? super T>
: 超类型通配符,匹配T
的某个父类型
2. <T extends E>
和 <? extends E>
到底有什么关系
<T extends E>
用于定义类型参数,它声明了一个类型参数T
,可放在泛型类定义中类名后面、泛型方法返回值前面<? extends E>
用于实例化类型参数,它用于实例化泛型变量中的类型参数,只是这个具体类型是未知的,只知道它是E
或E
的某个子类型
3. 用通配符形式重写下面方法声明代码
1 | public <T extends E> void addAll(DynamicArray<T> c) { |
1 | //泛型的通配符形式的类型参数 ? 可以写在方法参数里,而不用声明在方法返回值之前 |
4. 用通配符形式重写下面方法声明代码
1 | public static <T> int indexOf(DynamicArray<T> arr, Object obj) { |
1 | //泛型的通配符形式的类型参数 ? 可以写在方法参数里,而不用声明在方法返回值之前 |
5. 下面代码是否正确
1 | DynamicArray<Integer> ints = new DynamicArray<> (); |
- 三个
add()
方法都是非法的,Java 编译器会报错 - 上面通配符有一个重要的限制:只能读,不能写
- 如果允许写入, Java 就无法确保类型安全性,这显然违背了 Java 关于类型安全的承诺,所以 Java 干脆禁止
6. 下面代码是否正确,怎样解决
1 | public static void swap(DynamicArray<?> arr, int i, int j) { |
Java 编译报错
解决方法:借助带类型参数的泛型方法(Java 容器类中就有类似这样的用法,公共的 API 是通配符形式,形式更简单,但内部调用带类型参数的方法)
1
2
3
4
5
6
7
8
9private static <T> void swapInternal(DynamicArray<T> arr, int i, int j) {
T tmp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, tmp);
}
public static void swap(DynamicArray<?> arr, int i, int j) {
swapInternal(arr, i, j);
}
7. 使用通配符形式简化下面方法声明的代码
1 | //简化前 |
1 | //简化后 |
8. 下面代码能使用通配符优化吗,为什么
1 | public static <T extends Comparable<T>> T max(DynamicArray<T> arr) { |
- 不能
- 如果返回值依赖于类型参数,则不能用通配符
9. 泛型方法到底应该用通配符的形式还是加类型参数,两者到底有什么关系
- 通配符形式都可以用类型参数的形式来替代,通配符能做的,类型参数都能做
- 通配符形式可以减少类型参数,形式上往往更为简单,可读性也更好。所以,能用通配符的就用通配符
- 如果类型参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,则只能用类型参数
- 通配符形式和类型参数往往配合使用,比如,上面的
copy()
方法,定义必要的类型参数,使用通配符表达依赖,并接受更广泛的数据类型
10. 下面代码是否正确,怎样解决
1 | //在 DynamicArray 中添加一个方法 |
Java 编译报错。因为期望的类型是
DynamicArray<Integer>
,不是DynamicArray<Number>
解决方法:使用超类型通配符
1
2
3
4
5public void copyTo(DynamicArray<? super E> dest) {
for(int i = 0; i < size; i++) {
dest.add(get(i));
}
}
11. 下面代码是否正确,怎样解决
1 | class Base implements Comparable<Base> { |
- Java 编译报错,类型不匹配
- 原因是,我们可能会以为 Java 会将
max()
方法 的类型参数T
推断为Child
类型,但类型T
的要求是extends Comparable<T>
,而Child
并没有实现Comparable<Child>
,它实现的是Comparable<Base>
。 - 解决方法:使用超类型通配符,修改
max()
方法 声明,即:public static <T extends Comparable<? super T>> T max(DynamicArray<T> arr)
- 这么修改一下就可以了,这种写法比较抽象,将
T
替换为Child
,即:Child extends Comparable<? super Child>
,因为<? super Child>
可以匹配Base
,所以整体就是匹配的
12. 下面这种写法是否正确
1 | public <T super E> void copyTo(DynamicArray<T> dest) {} //非法 |
- Java 不支持这种语法
- 对于超类型通配符,不能用参数类型替代
- 类型参数限定只有
extends
形式,没有super
形式
- 对于有限定的通配符形式
<? extends E>
,可以用类型参数替代
13. 三种通配符的比较
- 它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型
<? super E>
用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代<?>
和<? extends E>
用于灵活读取,使得方法可以读取E
或E
的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简洁