在 Java 8(及其后续版本)的泛型中,? extends T 和 ? super T 是两种通配符,它们用于表示泛型的上界和下界。这两个通配符的主要目的是增加代码的灵活性和复用性。
这个通配符表示一个未知的类型,它是类型 T 或 T 的任何子类型。当你需要从泛型集合中读取数据,但不需要写入数据时,这个通配符很有用。例如:
import java.util.ArrayList; import java.util.List; public class Demo20240723162040 { public static void main(String[] args) { List<? extends Number> list = getData(); for(Number n : list) { // 强制类型转换,可能会出现 ClassCastException 异常 // 因为 Number 的子类不仅仅有 Integer,还有 Double 等等 Integer i = (Integer) n; System.out.println("n = " + n + ", i = " + i); } // 编译错误 // 因为当前 list 中的具体类型不知道,可能是 Integer 也可能是 Double // 因此,不允许向 list 中添加元素 //list.add(Integer.valueOf(10)); } private static List<? extends Number> getData() { List<Integer> data = new ArrayList<>(); data.add(1); data.add(2); data.add(3); return data; } }
在这个例子中,list 可以是 List<Integer>、List<Double> 或任何其他 Number 的子类型的列表。但是,你不能向这样的列表添加元素,因为编译器不知道列表的确切类型。
Number 类的直接子类如下图:
这个通配符表示一个未知的类型,它是类型 T 或 T 的任何父类型(包括 Object)。当你需要向泛型集合中写入数据,但不需要读取数据时,这个通配符很有用。
示例:
import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Demo20240723162951 { public static void main(String[] args) { List<? super Integer> list = getData(); list.add(4); // OK System.out.println(Arrays.toString(list.toArray())); //[1, 2, 3, 4] // 错误:编译器不知道 list 中存储的是什么类型,可能是 Integer,也可能是 Number // list 里面存储的 //Integer value1 = list.get(0); //Number value2 = list.get(0); // 如果我们明确知道类型,可以进行强制类型转换,或者探测 Object value = list.get(0); if(value instanceof Integer) { Integer value1 = (Integer)value; System.out.println(value1); //1 } else if(value instanceof Number) { Number value2 = (Number)value; System.out.println(value2); } } private static List<? super Integer> getData() { List<Integer> data = new ArrayList<>(); data.add(1); data.add(2); data.add(3); return data; } }
在这个例子中,list 可以是 List<Number>、List<Object> 或任何其他 Integer 的父类型的列表。你可以向这样的列表添加 Integer 或其任何子类型的对象。但是,你不能从这样的列表读取元素到一个具体的类型,因为编译器不知道列表的确切类型。 IDEA 会给出如下错误信息:
? extends T 允许你读取数据,但不允许你写入数据。因为它是协变(Covariance)的,如果对于类型 A 和 B,A 是 B 的子类型,并且 F[A] 是 F[B] 的子类型,那么 F 被称为是协变的。
? super T 允许你写入数据,但不允许你安全地读取数据,因为它是逆变(Contravariance)的,如果对于类型 A 和 B,A 是 B 的子类型,并且 F[B] 是 F[A] 的子类型,那么 F 被称为是逆变的。