基本概念
如果不使用通配符,那么我们在使用了泛型之后就不允许类型参数发生改变了,但是有了通配符就可以更加灵活的控制类型参数,类型参数可以发生改变。下面准备了3个类用于演示
public class A {
}
public class B extends A {
}
public class C extends B {
}
通配符extends
我们使用这个通配符可以限制类型参数是某个类(接口)或者是其子类。
public static <T extends AutoCloseable> void close(T t) {
try {
t.close();
} catch (Exception e) {
e.printStackTrace();
}
}
上面代码中我就限定了T是AutoCloseable的实现类,由于是它的实现类,那么AutoCloseable的方法当然也就可以调用了。
如果不需要用到T,那么就可以使用?代替,个数如下
public static void printList(List<? extends B> list) {
for (B b : list) {
System.out.println(b);
}
}
上面的List<? extends B>就代表传入的List只能是B类或其子类类型的集合
需要注意的是,对于extends,我们不能够填充元素,也就是写操作
public static void printList(List<? extends B> list) {
list.add(new B());
}
上面代码会出现错误
原因就是编译器知道要传入的参数是某个类型的子类型,但不知道具体是哪个子类型
举个例子就是List<? extends B>可以是List<B>和List<C>,假设D为B的子类,那么还可以为List<D>,以此类推,子类是无限制的,所以使用extends肯定就是不能进行写操作了,原因就是子类型无法引用父类型。好比一个List<C>存储一个B元素不合理吧
通配符super
这个通配符可以限制参数类型为某个类或者为其父类
public static void downLimit(List<? super B> list) {
// list.add(new C());
// list.add(new B());
for (Object o : list) {
System.out.println(o);
}
}
List<? super B>就表示传入的类型为B或B的父类的集合。对于super,由于不知道具体传入的是什么,所以就只能使用Object接收了。
对于super,我们可以填充元素
public static void downLimit(List<? super B> list) {
list.add(new C());
list.add(new B());
}
对于这种情况,可以理解为List<? super B>可以是List<B>或者List<A>或者其他的父类集合,那么我们我们不管哪种情况,存储B和C都是合理的吧,父类引用子类没有任何问题。
下面就是java核心卷中给出的总结
带有超类型限定的通配符允许你写入一个泛型对象,而带有子类型限定的通配符允许你读取一个泛型对象。
无限定通配符?
下面就来看一个例子分析一下一个Tool类
public class Tool<T> {
private T info;
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
}
对于Tool<?>会有以下2个方法
? getInf()
void setInfo(?)
对于getInfo,可以调用,返回值只能赋给Object。对于setInfo,根本不能调用(如果调用只能传入null),因为不知道?是什么。
对于?,通常都是为了避免使用泛型方法。<?>就表示匹配任意类型,和extends和super一起使用分别就表示上限和下限