、
126. Java 泛型 - 泛型、继承与子类型
1. 泛型与继承
在 Java 中,继承是指一个类(子类)能够从另一个类(父类)继承属性和方法。子类的对象可以赋值给父类类型的变量,这种行为通常称为“is-a”关系。例如,Integer 是 Number 的子类,Integer 是 Object 的子类。因此,可以将 Integer 赋给 Object 类型的变量:
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // OK,Integer 是 Object 的子类
这里的 someInteger 变量是 Integer 类型,然而它可以赋值给一个 Object 类型的变量,因为 Integer 是 Object 的子类,这符合 “is-a” 关系。
2. 泛型与继承
泛型的工作原理也与继承相似,但需要注意的是,泛型是不可变的。换句话说,泛型类的子类型和父类型之间并不是简单的继承关系。
示例: Number 和 Integer
我们知道 Integer 是 Number 的子类,因此以下代码是有效的:
public void someMethod(Number n) { /* ... */ }
someMethod(new Integer(10)); // OK,Integer 是 Number 的子类
someMethod(new Double(10.1)); // OK,Double 也是 Number 的子类
Integer 和 Double 都是 Number 类型的子类,因此它们可以作为 Number 类型的参数传递给方法。
泛型的子类型关系
但是当涉及到泛型类型时,情况稍有不同。尽管 Integer 是 Number 的子类,Box<Integer> 并不是 Box<Number> 的子类。这通常是使用泛型时的一个误解。
示例:泛型类型 Box
考虑以下方法:
public void boxTest(Box<Number> n) { /* ... */ }
这个方法接受一个 Box<Number> 类型的参数。根据泛型的规则,无法将 Box<Integer> 或 Box<Double> 传递给该方法,尽管 Integer 和 Double 都是 Number 的子类。这是因为泛型类型 Box<Integer> 和 Box<Number> 之间并没有继承关系,尽管它们的类型参数有继承关系。
Box<Number> numberBox = new Box<>();
Box<Integer> integerBox = new Box<>();
boxTest(numberBox); // OK
boxTest(integerBox); // 编译错误,Box<Integer> 不是 Box<Number> 的子类型
解释
尽管 Integer 是 Number 的子类,但是泛型的类型参数不具备继承关系。因此,Box<Integer> 不是 Box<Number> 的子类型。每种泛型类型都是一个独立的类型,它们之间没有任何继承或兼容关系。这是泛型的一大特殊之处,它与普通的类继承机制有所不同。
3. 泛型类型与通配符
为了让泛型类型更具灵活性,Java 引入了通配符(?),它使得我们可以在不知道类型的情况下,处理泛型对象。最常见的使用通配符的场景是通过限制通配符的边界来实现类型的兼容性。
示例:通配符与继承
我们可以使用通配符来实现对 Box<Number> 或其子类型(如 Box<Integer> 或 Box<Double>)的兼容:
public void boxTest(Box<? extends Number> n) { /* ... */ }
使用 Box<? extends Number> 类型作为参数,可以接收 Box<Number>、Box<Integer>、Box<Double> 等类型,这样就能处理所有 Number 类型的子类了:
Box<Number> numberBox = new Box<>();
Box<Integer> integerBox = new Box<>();
Box<Double> doubleBox = new Box<>();
boxTest(numberBox); // OK
boxTest(integerBox); // OK
boxTest(doubleBox); // OK
这样做通过通配符提供了更大的灵活性,同时仍然保持了类型安全。
4. 总结
- 继承与泛型:在普通的类继承中,一个类的子类可以赋值给父类的变量,但泛型类型之间并没有继承关系。即使类型参数之间有继承关系,泛型类型本身不会自动形成继承关系。
- 泛型类型不具备继承关系:例如,
Box<Integer>不是Box<Number>的子类,尽管Integer是Number的子类。泛型类型是独立的,不能像普通的类那样使用继承。 - 通配符的使用:为了在泛型中处理继承关系,可以使用通配符(如
? extends T)来实现对父类和子类的兼容,增强灵活性。