Java泛型中的协变与逆变是很重要的概念,也常被面试官问到。本篇文章将带你深入理解这些概念,并介绍有关extends和super的最佳实践。
什么是Java泛型中的协变和逆变?
在介绍协变和逆变之前,我们需要先了解两个术语: 子类型和超类型。子类型是指一个类派生自另一个类,而超类型是指另一个类派生自某一个类。这两个概念很重要,后面我们会经常用到。
在Java中,泛型支持协变和逆变。通俗点讲,协变是指“向上转型”,逆变是指“向下转型”。具体来说,协变支持超类型的引用指向子类型的对象,而逆变支持子类型的引用指向超类型的对象。这是因为Java泛型中,如果一个类是另一个类或接口的子类型,则相应的泛型也应该是可以兼容的。
Java泛型中extends和super的选择
Java中,有两种方法可以声明泛型类型的边界,即extends和super。extends表示泛型类型应该是其边界类型的子类型,而super表示泛型类型应该是其边界类型的超类型。在使用泛型时,我们需要根据具体情况来选择使用哪种方式。
使用extends
在定义泛型时,如果希望只接受某个类及其子类作为泛型类型参数,就可以使用extends关键字。例如:
public class Box<T extends Number> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
在这个Box类中,我们使用了extends关键字来限制泛型类型T必须是Number及其子类类型。这意味着,我们可以使用任何Number的子类实例来创建Box对象。例如:
Box<Integer> integerBox = new Box<Integer>();
extends关键字的使用场景包括:
- 如果希望某个泛型类型参数只能接受其边界类型及其子类型。
- 如果希望某个方法返回的类型是某个泛型类型参数的子类型。
使用super
和extends相反,如果希望只接受某个类及其超类作为泛型类型参数,就可以使用super关键字。例如:
public class Box<T super Number> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
在这个Box类中,我们使用了super关键字来限制泛型类型T只能是Number及其父类类型。这意味着,我们可以使用任何Number的父类实例来创建Box对象。例如:
Box<Object> objectBox = new Box<Object>();
super关键字的使用场景包括:
- 如果希望某个泛型类型参数只能接受其边界类型及其超类。
- 如果希望把某个泛型类型参数的超类型传递给某个方法。
两个示例说明
为了深入理解协变和逆变,我们来看两个示例。
示例1: 协变
假设我们有一个Shape类和一个Circle类,Circle类是Shape类的子类。现在我们需要将Circle类的实例添加到一个List中。我们可以这样定义这个List:
List<? extends Shape> shapes = new ArrayList<Circle>();
这个使用了extends关键字的定义表明,shapes可以指向一个List
shapes.add(new Circle());
shapes.add(new Shape());
示例2: 逆变
假设我们有一个Comparator接口,其中有一个compare方法:
public interface Comparator<T> {
public int compare(T o1, T o2);
}
现在我们需要在一个GeometricShape类中实现一个Comparator接口的compare方法。由于Circle类是GeometricShape类的子类,我们希望能够使用一个比较圆形的比较器来比较GeometricShape类的实例。我们可以这样定义比较器:
public class CircleComparator implements Comparator<Circle> {
public int compare(Circle c1, Circle c2) {
// ...
}
}
但这个比较器无法在GeometricShape类中实际使用,因为在Comparator接口和CircleComparator类之间不存在任何继承关系。为了解决这个问题,我们可以在GeometricShape类中使用一个接受Comparator<? super T>类型参数的sort()方法:
public abstract class GeometricShape {
public abstract void draw();
public static <T extends GeometricShape> void sort(List<T> shapes, Comparator<? super T> comparator) {
// ...
}
}
由于使用了super关键字,这个sort()方法可以接受任何比较器,只要它可以比较T的超类型。这意味着,我们可以这样调用我们的sort()方法:
GeometricShape.sort(shapes, new Comparator<Circle>() {
public int compare(Circle c1, Circle c2) {
// ...
}
});
这里的sort()方法参数中,shapes是一个List
结论
Java泛型中的协变和逆变是很重要的概念,也常被面试官问到。本篇文章介绍了什么是协变和逆变,并介绍了使用extends和super的最佳实践。请务必根据具体的场景,选择正确的关键字来定义泛型类型的边界。同时,也要时刻记得在使用协变和逆变时,尽可能地让泛型类型参数更加通用,以便兼容更多的子类型和超类型。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java泛型之协变与逆变及extends与super选择 - Python技术站