ArrayList 包含同一个超类的不同对象 - 如何访问子类的方法

Posted

技术标签:

【中文标题】ArrayList 包含同一个超类的不同对象 - 如何访问子类的方法【英文标题】:ArrayList containing different objects of the same superclass - how to access method of a subclass 【发布时间】:2013-04-06 09:47:42 【问题描述】:

嗨,我想知道是否有一个简单的解决方案可以解决我的问题,

我有一个ArrayList

ArrayList <Animal> animalList = new ArrayList<Animal>(); 

/* I add some objects from subclasses of Animal */

animalList.add(new Reptile());
animalList.add(new Bird());
animalList.add(new Amphibian());

它们都实现了一个方法move() - 当move() 被调用时,Bird 会飞起来。 我知道我可以使用 this 访问超类的常用方法和属性

public void feed(Integer animalIndex) 
    Animal aAnimal = (Animal) this.animalList.get(animalIndex);
    aAnimal.eat();

没关系 - 但现在我想访问子类 Bird 拥有的 move() 方法。 我可以通过将Animal 转换为Bird 来做到这一点:

Bird aBird = (Bird) this.animalList.get(animalIndex);
aBird.move();

在我的情况下,我不想这样做,因为这意味着我对Animal 的每个子类型都有 3 组不同的上述代码。

好像有点多余,有没有更好的办法?

【问题讨论】:

为什么 Animal 没有你在子类中重写的 move() 方法? ... 呼叫移动?如果它没有调用子类实现,那么你没有告诉我们一些事情。 @user2278797 让您知道:我编辑了您的问题以获得更好的格式并更改了类的名称(因为它们在您的代码中不一致):我从他们的名称中删除了 s-es在它们被添加到animalList 的行中。他们是:animalList.add(new Reptiles()); animalList.add(new Birds()); animalList.add(new Amphibians()); 现在是:animalList.add(new Reptile()); animalList.add(new Bird()); animalList.add(new Amphibian());。如果您在自己的任何代码中使用它们,请确保相应地更改它们或重新编辑您问题的代码。 我非常困惑为什么这被如此赞成,因为这个问题是无稽之谈,并且暗示了对多态性概念的误解。似乎 OP 认为在第一块代码中的 (Animal) 强制转换将导致调用超类的 move() 实现。它不会,因为对象实际是的子类的覆盖move() 将被调用。换句话说,(Animal) 类是完全多余的,Java 语言已经提供了所要求的确切功能而无需更改任何内容。 【参考方案1】:

在您的情况下,以下可能有效,但时间复杂度为 O(n):

public void moveBird()
    for(Animal aminal:animalList)
        if(animal instanceof Bird)
            aninmal.move();
        
    

【讨论】:

我假设每个 Animal 都有一个 move() 方法。【参考方案2】:

你不需要做任何铸造。被覆盖的方法应该被称为[简单多态]

Animal aAnimal==  this.animalList.get(animalIndex);
aAnimal.move();

如果对象是鸟,上面的代码应该调用bird方法,不是吗?

而且投射不是解决方案,你将如何决定投射哪个对象?您将不得不使用 instanceOf。

【讨论】:

谢谢,可能是一个可能的解决方案。我不确定我理解你的意思 And cast is not a solution ,你将如何决定要投射哪个对象?您将不得不使用 instanceOf" ,用户将单击一个对象,即动物,这将传递它在 arrayList 中放置位置的索引号。干杯 所以你这样做:animalList.add(new Reptile());动物列表.add(新鸟());动物列表.add(新两栖动物());现在,在检索对象时,您必须跟踪哪个对象在哪个位置进行正确的转换,或者您需要 instanceof 运算符来检查,否则您最终可能会将 Amphibian 转换为 Bird。你明白我的意思了吗? 好吧,我想是这样 - 如果我这样做了 好吧,我想是这样 - 如果我这样做了: public void createAnimal() Bird b = new Bird(); this.animalList.add(b); - 是的,我仍然不确定我是否可以这样跟踪它们。 你不能没有办法。您遇到了在泛型之前存在的情况,我们必须使用 instanceof 来跟踪类型。【参考方案3】:

从超类中确实没有很好的方法来做到这一点,因为每个子类的行为都会不同。

为确保您实际调用了适当的move 方法,请将Animal 从超类更改为接口。然后,当您调用move 方法时,您将能够确保为您想要的对象调用适当的移动方法。

如果您希望保留公共字段,则可以定义一个抽象类 AnimalBase,并要求所有动物都以此为基础,但每个实现都需要实现 Animal 接口。

例子:

public abstract class AnimalBase 
    private String name;
    private int age;
    private boolean gender;

    // getters and setters for the above are good to have here


public interface Animal 
    public void move();
    public void eat();
    public void sleep();


// The below won't compile because the contract for the interface changed.
// You'll have to implement eat and sleep for each object.

public class Reptiles extends AnimalBase implements Animal 
    public void move() 
        System.out.println("Slither!");
    


public class Birds extends AnimalBase implements Animal 
    public void move() 
        System.out.println("Flap flap!");
    


public class Amphibians extends AnimalBase implements Animal 
    public void move() 
        System.out.println("Some sort of moving sound...");
    


// in some method, you'll be calling the below

List<Animal> animalList = new ArrayList<>();

animalList.add(new Reptiles());
animalList.add(new Amphibians());
animalList.add(new Birds());

// call your method without fear of it being generic

for(Animal a : animalList) 
    a.move();

【讨论】:

好的,但是子类之间共享的所有常用方法和行为呢?在每个单独的子类中声明和描述相同事物的相同方法和属性不是多余的吗?例如,我有一些常见的属性名称、性别、颜色等,然后​​还有一些方法,比如吃和睡。顺便感谢您的快速回复。 此时,您可以拥有一个名为AnimalBase 的抽象对象,它提供您需要的字段。然后你会想要扩展它,并实现Animal 接口。 谢谢 Makoto,请问有没有更正确的方法来解决这个问题?例如使用基本抽象类并实现其他方法更好,还是将某些方法留空并为每个子类覆盖它们会更好?干杯 使用至少可以确保方法在编译时存在的接口是最好的方法。这就是我在这里应用的方法。抽象类是为了绕过在每个子类中一遍又一遍地创建相同的字段。 感谢诚。我已经尝试过了,它很有效,所以谢谢。如果我想访问 Reptile 类的特定属性怎么办?我不想这样做,但也许我需要为某些方法添加一个 if 语句,以找出 a 到底是什么类型的动物。例如如果(a instanceof bird)然后转换为一个鸟对象并访问 maxFlightDistance 属性,如果(a instanceof reptile)转换为 reptile 并访问 reptile 属性。这会起作用,但是回到我原来的问题,每次我都这样做似乎是多余的需要弄清楚我使用的是什么类型的动物。你怎么看?【参考方案4】:
Bird getMyBird(Integer aniInteger) 
        Bird b = new Bird();
        //Do somthig with bird object...

        return b;
        //get your modifeid bird object
    

    Bird myBird = animalList.get(animalIndex);

    myBird.move();

【讨论】:

你在用这个做什么?我想做的就是在任何类型的Animal 上调用move。你为什么这么关注Bird 是的,这里没有关注鸟类——只是用它作为例子。

以上是关于ArrayList 包含同一个超类的不同对象 - 如何访问子类的方法的主要内容,如果未能解决你的问题,请参考以下文章

无法在初始化为超类的 ArrayList 中调用派生类的方法

Java 只返回超类的属性

对象超类的连接信号

Java中类的继承

子类会在后台包含来自超类的私有方法和成员变量吗? [复制]

什么体现了类的多态性?