如何在Java中覆盖通用列表返回类型

5

我正在尝试覆盖子类中返回类型为列表的方法,如下例所示。由于泛型问题,我无法在子类中执行此操作。我无法更改我的超类代码,那么我该如何解决这个问题?请有经验的人指导一下...非常感谢。

无法更新的超类:

public class Animal {
           private String legs;
    }

public class TestSuper {

    public List<Animal> addAnimals() {
        return animals;
    }
}

子类:

public class Dog extends Animal {
         private String sound;
    }

public class TestSub extends TestSuper {

    public List<Dog> addAnimals() { --> I need to override this method but want return type as List of dogs
        return null;
    }
}

1
你只能使用与赋值兼容的类型来覆盖返回类型。然而,泛型不是协变的。List<Dog> 不是 List<Animal> 的子类型。请参见此线程 - Ted Hopp
5个回答

12
如果超类真的无法更新,恐怕您就无能为力了。
如果您能够更新超类:
在TestSuper中,您将使用public List addAnimals()而不是public List addAnimals()。
您还可以使用这样的实用方法:
@SuppressWarnings("unchecked")
public static <T extends Animal> List<T> cast(List<Animal> animals, Class<T> subclass) {
    List<T> out = new ArrayList<T>();
    for (Animal animal : animals) {
        if (!subclass.isAssignableFrom(animal.getClass())) {
            // the "animal" entry isn't an instance of "subclass"
            // manage this case however you want ;)
        } else {
            out.add((T) animal);
        }
    }
    return out;
}

接下来,进行调用:

List<Dog> dogs = TheClassNameYouChose.cast(animals, Dog.class);

谢谢您的回复,但是我的项目中没有那个选项,因此期望得到一些建议:( - Satya
我想使用超类中的addAnimals()方法...因此我正在尝试覆盖它。 - Satya
@Satya 是的,我明白了,这个想法很好,但是如果你不能改变 TestSuper#addAnimals() 方法的签名,那就根本不可能... - sp00m

3
如果DogAnimal的子类,你可以尝试使用以下签名:
public List<? extends Animal> addAnimals()

然后执行。
public List<Dog> addAnimals()

在子类中。

或者您可以将超类设置为通用类型 <T>,并将实现类设置为 <Dog>


2

你不能将List的通用类型更改为该通用类型的子类型。问题在于对任何实例的引用都可以是该实例的子类型。因此,让我们来看一下这个例子

SuperType sup = new SubType();
sup.getAnimals().add(new Cat());
//adding cat is possible because getAnimals() returns List<Animal>

所以,你要将Cat添加到只应包含Dogs的列表中,这是错误的。


同样,在重写的方法中,你不能将泛型类型的列表更改为具有该泛型类型的超类型的列表(例如,将List<Dog>更改为List<Animal>)。为什么呢?让我们看一个例子

SuperType sup = new SubType();
Dog d = sup.getAnimals().get(0);

在子类型中,我们有动物列表,因此可能会出现get(0)返回猫的情况。
如果您可以更改类中的代码,则可以尝试使用以下方法。
class Super<T extends Animal> {
    public List<T> addAnimals() {
        return null;
    }
}

class Sub extends Super<Dog> {

    @Override
    public List<Dog> addAnimals() {
        return null;
    }
}

0

你需要使用泛型来使TestSuper正确地工作。

TestSuper<T extends Animal> {

  public List<T> addAnimals() {

  }
}

TestSub extends TestSuper<Dog> {


}

0

这是不可能的。超类合约规定,如果你有:

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

以下代码必须有效:

TestSuper test = new AnySubclassOfTestSuper(); // including TestSub!
test.addAnimals().add(new Cat());

你在TestSub类中的覆盖会违反这个规定,因为你不能将Cat添加到List<Dog>中。由于这是面向对象类型系统中一个相当基本的原则的违反,你不太可能找到一个完美的解决办法来绕过这个限制。

将超类更改为使用List<? extends Animal>的建议是可行的,因为你无法向该列表中添加任何内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接