关于这里的<?>/ <? extends ClassName>/<? super ClassName>
,视频提到<? extends ClassName>
只能读不能写,<? super ClassName>
可以读可以写,我给出比较正式的解释。
首先是通配符捕获这个知识:我认为通配符捕获发生在实例化一个对象,并将它赋值给一个带有泛型参数的类名变量,而泛型参数中包含通配符,且泛型变量只是形如<?>/<? extends ClassName>/<? super ClassName>
(形如ClassName<?>
)而不含嵌套(不是ClassName<AnotherName<?>>
类似结构),此时通过类名变量调用包含泛型的方法时就会触发通配符捕获。
即:通过ClassName<?> / ClassName<? extends AnotherClassName>/ClassName<? super AnotherClassName>
类型的变量调用非泛型方法,且方法中的参数含有泛型参数,就会触发通配符捕获,此时需要能确定参数的类型。
例如:
List<? extends Employee> list = null;
list.add(new Object(); // Error
// List中的add方法原型
public interface List<E> extends Collection<E>{
...
boolean add(E e);
...
}
分析super和extends的读写问题:
-
对于
ClassName<? extends ClassName> inst
调用的非泛型方法含泛型变量T
时,无法确定。对于任意一个类,总存在一个它的子类,所以无法保证传入的类实例满足此处参数为? extends ClassName
类集合中的任意一个的要求,所以只能通过传递null
来调用方法。 -
对于
ClassName<? super ClassName> inst
调用的非泛型方法含泛型变量T
时,无法确定。当传入的是ClassName
的类/子类的实例,就可以保证传入的实例满足此处参数为? super ClassName
类集合中的任意一个的要求。
举例:
class MyClass<T> {
void myMethod(List<T> list) {
// ...
}
public static void main(String[] args) {
MyClass<? extends Employee> myclass = new MyClass<>();
myclass.myMethod(new Object());// Error1
List<? extends Employee> list = null;
myclass.myMethod(list);// Error2
}
}
报错内容:
Error1
Error2:
可以看到这里触发了通配符捕获,但是如上所述,只能提供null进行调用。
下面是一个没有触发通配符捕获的案例:
// 方法原型:
default Predicate<T> and(Predicate<? super T> other) ;
// Main
List<Employee> list = new ArrayList<>();
list.add(new Employee());
//
Predicate<Manager> filter = (obj)->{
return obj.hashCode() % 2 == 0;
};
list.removeIf(filter); // Error, 替换为<Object> 、 <Employee>