[英]Java Generics of the form <T extends A<T>> and Inheritance
我对Java泛型有一个棘手的问题,或者我看不到树木的木材。
我有三个类,A,B和C,如下所示。
abstract class A<T extends A<T>> {
abstract T sefl();
};
abstract class B<T extends B<T>> extends A<T> {
};
class C extends B<C> {
@Override
C sefl() {
return this;
}
}
后来我有不同版本的B以及不同版本的C。 此外,我有一个功能测试,应该接受B(或其表兄弟)的列表。 或者一般来说,它应该接受从A继承的任何元素列表。不幸的是,我需要知道函数体中列表元素的类型,即a.self可以返回的最高类型(或类型T)。 功能测试如下:
static <T extends A<T>> void test(List<T> list) {
for (A<T> a : list) {
@SuppressWarnings("unused")
T t = a.sefl();
}
}
现在,使用C的列表调用test。
List<C> cs = new LinkedList<C>();
test(cs);
但
List<B> bs = new LinkedList<B>();
test(bs);
导致警告需要类型参数和
List<B<?>> bs = new LinkedList<B<?>>();
test(bs);
无效。 我的错误在哪里,或者如何创建功能测试接受的B列表?
这个问题背后的动机有些话。 A,B和C类(或Animal,Mammal和Cat)实现了树状数据结构,其中每个类都使用一些属性扩展结构。 通常,所有超类都是抽象的,您只能从叶类创建实例,例如cat。 现在,困难在于类实现了写时复制策略(COW),即修改对象会创建并使用modified属性返回自身的新实例。
例如,假设所有动物都有年龄属性。 您可以在Animal中轻松定义此属性,并且可以提供getter方法来返回年龄。
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
};
但是,如何定义setter方法? 您可以按如下方式编写它:
public abstract Animal setAge();
这要求(至少)每个非抽象元素必须实现setter函数。 例如:
class Cat extends Mammal<C> {
@Override
public Animal setAge(int age) {
return new Cat(/* .. */);
}
}
请记住,在我们实施COW策略时,我们必须创建一个新实例。 因此,在setter函数中(例如在Cat中实现),我们返回一个具有新时代的新猫。 在Cat元素上调用cat.setAge(4)会返回一个新的Cat。 不幸的是,由于类型签名,我们现在只有从setAge返回一个Animal,即使我们直接在Cat上调用它。 使用泛型的扭曲有助于在调用setAge时显示具体类型。 所以,我们可以像这样构建Animal:
abstract class Animal<T extends Animal<T>> {
private int age;
public int getAge(int age) {
return age;
}
public abstract T setAge();
};
在Cat我们可以说:
class Cat extends Mammal<C> {
@Override
public Cat setAge(int age) {
return new Cat(/* .. */);
}
}
所以,回到问题。 你的权利,使用List<? extends Animal<?>>
List<? extends Animal<?>>
作为列表的类型工作,但不幸的是,我需要一些方法来了解元素的类型。 或者更具体:功能测试必须用新元素替换旧元素。 例如:
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
@SuppressWarnings("unused")
Animal<?> a = animal.setAge(4711);
list.add(a);
}
}
不幸的是,列表扩展list.add(a); 是不符合此签名的声明。
嗯,它们是两个非常不同的实现:
class C ...
和
class B<T extends B<T>> ...
C
类没有声明任何泛型类型。
类名的简单字母在这里有点令人困惑,所以让我们这样做:
abstract class Animal<T extends Animal<T>> {
abstract T sefl();
};
abstract class Mammal<T extends Mammal<T>> extends Animal<T> {
};
class Cat extends Mammal<Cat> {
@Override
Cat sefl() {
return this;
}
}
所以:
List<Cat> catList = new LinkedList<>();
效果很好,因为没有涉及泛型类型。 编译器确定了
Cat extends Mammal<Cat> ( == Cat extends Animal<Cat> )
适合界限<T extends Animal<T>>
另一方面
List<Mammal> mammalList = new LinkedList<>();
test(mammalList); // ok, but mammal list of what???
编译器无法匹配有界类型。
事实上, Mammal<T extends Mammal<T>> extends Animal<T>
与<T extends Animal<T>>
没有任何关系。
即使提供通配符,您也永远无法通过List<Mammal<?>
进行test
。 方法签名拒绝它!
可能的解决方案:
一种更通用的测试方法
static void test2(List<? extends Animal<?>> list) {
for (Animal<?> animal : list) {
Animal a = animal.sefl();
}
}
可以与不同的List
类型一起使用:
List<? extends Mammal<?>> bs = new LinkedList<>();
test2(bs);
List<Cat> catList = new LinkedList<>();
test2(catList);
List<Animal<Cat>> animalList = new LinkedList<>();
test2(animalList);
Java版本:
java 9.0.4
Java(TM) SE Runtime Environment (build 9.0.4+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.4+11, mixed mode)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.