简体   繁体   English

形式<T的Java泛型扩展了A <T >>和继承

[英]Java Generics of the form <T extends A<T>> and Inheritance

I have a tricky problem with Java generics, or maybe I can't see the wood for the trees. 我对Java泛型有一个棘手的问题,或者我看不到树木的木材。

I have three classes, A, B, and C that look as follows. 我有三个类,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;
    }
}

Later I have different versions of B's as well as different versions of C's. 后来我有不同版本的B以及不同版本的C。 Furthermore, I have a function test that should accept a list of B's (or of one of its cousins). 此外,我有一个功能测试,应该接受B(或其表兄弟)的列表。 Or in general, it should accept any list of elements that inherit from A. Unfortunately, I need to know the type of the list's elements in the function's body, ie the uppermost type a.self can return (or the type T). 或者一般来说,它应该接受从A继承的任何元素列表。不幸的是,我需要知道函数体中列表元素的类型,即a.self可以返回的最高类型(或类型T)。 Function test looks as follows: 功能测试如下:

static <T extends A<T>> void test(List<T> list) {

    for (A<T> a : list) {
        @SuppressWarnings("unused")
        T t = a.sefl();
    }

}

Now, calling test with a list of C's works. 现在,使用C的列表调用test。

List<C> cs = new LinkedList<C>();
test(cs);

But

List<B> bs = new LinkedList<B>();
test(bs);

results in a warning requiring a type parameter and 导致警告需要类型参数和

List<B<?>> bs = new LinkedList<B<?>>();
test(bs);

is not valid. 无效。 Where is my mistake, or how can I create a list of B's that is accepted by function test? 我的错误在哪里,或者如何创建功能测试接受的B列表?


Some words to the motivation behind this problem. 这个问题背后的动机有些话。 The classes A, B, and C (or Animal, Mammal, and Cat) implement a tree-like data structure, where each class extends the structure with some properties. A,B和C类(或Animal,Mammal和Cat)实现了树状数据结构,其中每个类都使用一些属性扩展结构。 Typically, all super-classes are abstract and you can only create instances from leaf-classes, eg cat. 通常,所有超类都是抽象的,您只能从叶类创建实例,例如cat。 Now, the difficulty is that the classes implement a copy-on-write policy (COW), ie modifying an object creates and returns a new instance of itself with the modified property. 现在,困难在于类实现了写时复制策略(COW),即修改对象会创建并使用modified属性返回自身的新实例。

For example, let's say all animals have an age property. 例如,假设所有动物都有年龄属性。 You can easily define this property in Animal, and you can provide a getter method to return the age. 您可以在Animal中轻松定义此属性,并且可以提供getter方法来返回年龄。

abstract class Animal<T extends Animal<T>> {
    private int age;
    public int getAge(int age) {
        return age;
    }
};

However, how do you define the setter method? 但是,如何定义setter方法? You can write it as follows: 您可以按如下方式编写它:

public abstract Animal setAge();

This requires that (at least) each non-abstract element must implement the setter function. 这要求(至少)每个非抽象元素必须实现setter函数。 For example: 例如:

class Cat extends Mammal<C> {

    @Override
    public Animal setAge(int age) {
        return new Cat(/* .. */);
    }
}

Remember, as we implement a COW policy, we must create a new instance. 请记住,在我们实施COW策略时,我们必须创建一个新实例。 So, in the setter function (eg implement in Cat) we return a new cat with the new age. 因此,在setter函数中(例如在Cat中实现),我们返回一个具有新时代的新猫。 Calling cat.setAge(4) on a Cat element returns a new Cat. 在Cat元素上调用cat.setAge(4)会返回一个新的Cat。 Unfortunately, because of the type signature, we only now that we got an Animal returned from setAge, even if we call it on a Cat directly. 不幸的是,由于类型签名,我们现在只有从setAge返回一个Animal,即使我们直接在Cat上调用它。 The twist with the generics helps to reveal the concrete type when calling setAge. 使用泛型的扭曲有助于在调用setAge时显示具体类型。 So, we can construct Animal like this: 所以,我们可以像这样构建Animal:

abstract class Animal<T extends Animal<T>> {
    private int age;
    public int getAge(int age) {
        return age;
    }
    public abstract T setAge();
};

And in Cat we can say: 在Cat我们可以说:

class Cat extends Mammal<C> {

    @Override
    public Cat setAge(int age) {
        return new Cat(/* .. */);
    }
}

So, back to the problem. 所以,回到问题。 Your right, using List<? extends Animal<?>> 你的权利,使用List<? extends Animal<?>> List<? extends Animal<?>> as the type of the list works, but unfortunately, I need some way to know the type of the elements. List<? extends Animal<?>>作为列表的类型工作,但不幸的是,我需要一些方法来了解元素的类型。 Or more concrete: Function test must replace the old element with the new one. 或者更具体:功能测试必须用新元素替换旧元素。 For example: 例如:

static void test2(List<? extends Animal<?>> list) {
    for (Animal<?> animal : list) {
        @SuppressWarnings("unused")
        Animal<?> a = animal.setAge(4711);
        list.add(a);
    }
}

And unfortunately, the list extension list.add(a); 不幸的是,列表扩展list.add(a); ist the statement that doesn't work with this signature. 是不符合此签名的声明。

Well, they are two very different implementations: 嗯,它们是两个非常不同的实现:

class C ...

and

class B<T extends B<T>> ...

The class C doesn't declare any generic type. C类没有声明任何泛型类型。


Simple letters for class names are a bit confusing here, so let's do: 类名的简单字母在这里有点令人困惑,所以让我们这样做:

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;
    }
}

So: 所以:

List<Cat> catList = new LinkedList<>();

works well, as there is no generic type involved. 效果很好,因为没有涉及泛型类型。 The compiler determines that 编译器确定了

Cat extends Mammal<Cat> ( == Cat extends Animal<Cat> )

fits within the bounds <T extends Animal<T>> 适合界限<T extends Animal<T>>

On the other hand for 另一方面

List<Mammal> mammalList = new LinkedList<>();
test(mammalList); // ok, but mammal list of what???

the compiler can't match the bounded types. 编译器无法匹配有界类型。

In fact, Mammal<T extends Mammal<T>> extends Animal<T> doesn't have anything to do with <T extends Animal<T>> . 事实上, Mammal<T extends Mammal<T>> extends Animal<T><T extends Animal<T>>没有任何关系。

Even by providing a wildcard, you'll never be able to pass a List<Mammal<?> to test . 即使提供通配符,您也永远无法通过List<Mammal<?>进行test The method signature rejects it! 方法签名拒绝它!


A possible solution: 可能的解决方案:

A more generic test method 一种更通用的测试方法

static void test2(List<? extends Animal<?>> list) {
    for (Animal<?> animal : list) {
        Animal a = animal.sefl();
    }
}

can be used along with different List types: 可以与不同的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 version: 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM