本文共 3471 字,大约阅读时间需要 11 分钟。
在编写Java程序时,我们有时需要处理不同类型的对象,但又希望能够在不暴露具体类型的情况下进行操作。Java 提供了泛型类型通配符,用于实现这种需求。本文将详细介绍类型通配符的使用方法及其相关限制。
在编写泛型类时,通常会遇到以下场景:
假设我们有一个抽象类 Shape,其中定义了一个抽象方法 draw(Canvas c)。然后,具体实现该方法的类有 Circle 和 Rectangle。我们希望创建一个 Canvas 类,其中有一个 drawAll(List shapes) 方法,能够接受任何类型的 Shape 实例。
原始代码中,drawAll() 方法的参数类型被限定为 List<Shape>,因此只能接受 Shape 类型的列表。然而,实际上,我们希望它能够接受任何 Shape 的子类类型的列表,例如 List<Circle> 或 List<Rectangle>。
我们可以将 List 的通配符修改为 List<? extends Shape>。这样,drawAll() 方法就可以接受任何类型的 Shape 实例的列表。这种通配符被称为上界通配符。
? extends Type)上界通配符允许我们指定通配符的上界,即通配符可以接受的具体类型及其子类。例如:
List shapes = new ArrayList<>();shapes.add(new Circle());shapes.add(new Rectangle());
在这种情况下,shapes 列表可以包含 Shape 类型及其任意子类(如 Circle 或 Rectangle)。
? super Type)下界通配符允许我们指定通配符的下界,即通配符只能接受某个具体类型或其父类。例如:
List numbers = new ArrayList<>();numbers.add(new Integer(10));numbers.add(new Double(3.14));
在这种情况下,numbers 列表可以包含 Number 类型及其父类(如 Object)。
?)无限定通配符允许通配符接受任何类型,但在进行写操作时会带来潜在的类型安全风险。例如:
List list = new ArrayList<>();list.add(new Object());Object obj = list.get(0);
这里,obj 的类型会被推断为 Object,但无法确保其实际类型是否与 List 中的元素类型一致。
考虑一个 Canvas 类,其中有一个 drawAll(List<? extends Shape> shapes) 方法。我们可以将任何类型的 Shape 实例的列表传递给这个方法:
Listcircles = new ArrayList<>();circles.add(new Circle());drawAll(circles);
此时,shapes 的类型被推断为 List<Circle>,满足 Shape 的上界要求。
考虑一个 Census 类,其中有一个 addRegistry(Map<String, ? extends Person> registry) 方法。我们可以将任意类型的 Person 实例的映射传递给这个方法:
Mapdrivers = new HashMap<>();drivers.put("张三", new Driver());addRegistry(drivers);
此时,registry 的类型被推断为 Map<String, Driver>,满足 Person 的下界要求。
以下代码会导致编译错误:
List shapes = new ArrayList<>();shapes.add(new Rectangle());
原因是 Rectangle 是 Shape 的子类,满足上界要求。但如果尝试传入非 Shape 类型的实例,会导致错误。
public class Pair{ private T first; private T last; public Pair(T first, T last) { this.first = first; this.last = last; } public T getFirst() { return first; } public T getLast() { return last; } public void setFirst(T first) { this.first = first; } public void setLast(T last) { this.last = last; }}
PairintegerPair = new Pair<>(123, 456);Pair stringPair = new Pair<>("test", "test");integerPair.setFirst(456);stringPair.setLast("test2");
integerPair 的类型是 Pair<Integer>,只能存储 Integer 类型的对象。setFirst 和 setLast 方法可以传递任意类型的实例,只要它们是目标类型的子类。public class TestExtends { public static void main(String[] args) { Pair p = new Pair<>(123, 456); printGetValues(p); } static int add(Pair p) { Number first = p.getFirst(); Number last = p.getLast(); return first.intValue() + last.intValue(); } static void printGetValues(Pair p) { Object obj = p.getFirst(); System.out.println(obj.getClass().getName()); }} add 方法可以接受任何类型的 Number 实例的 Pair。printGetValues 方法只能打印 Pair 中的元素值,无法获取其具体类型。通过上述案例可以看出,类型通配符在Java 中提供了极大的灵活性。上界通配符允许我们指定通配符的上界,确保通配符只能接受特定类型及其子类。下界通配符则允许我们指定通配符的下界,确保通配符只能接受某个类型及其父类。无限定通配符虽然灵活,但需要谨慎使用,以避免类型安全问题。
在实际开发中,选择合适的通配符类型对于代码的可读性和维护性至关重要。同时,需要注意通配符的边界条件,以确保代码的类型安全。
转载地址:http://bozt.baihongyu.com/