【问题标题】:Access a subclass method through an ArrayList of superclass objects?通过超类对象的 ArrayList 访问子类方法?
【发布时间】:2016-01-16 14:34:34
【问题描述】:

我有一个超类(动物)和两个子类(青蛙和狮子)。在下面我的私人方法中,我正在创建三只狮子和一只青蛙。我在第一个循环中显示了所有四个动物的体重,在第二个循环中显示了所有狮子的名字。我得到了正确的输出,但我的老师告诉我“没有必要在你的 main 方法中创建两个 ArrayLists”。我猜他是在建议我使用我的第一个 ArrayList 来访问我的 Lion 子类的 getName() 方法。

我就是这样做的,而且效果很好:

 import java.util.ArrayList;

    public class AnimalTest {

        public static void main(String[] args){
            ArrayList<Animal> animalList = createList();

            for(int i = 0; i < animalList.size(); i++){
                System.out.println(animalList.get(i).getWeight() + " lbs");
            } 

            ArrayList<Lion> lionList = createList();
            for(int j = 0; j < 3; j++){
                System.out.println(lionList.get(j).getName());
            }

        }

        private static ArrayList createList(){
            ArrayList<Animal> list = new ArrayList<Animal>();

            list.add(new Lion("Leo I", 530.00));
            list.add(new Lion("Leo II", 560.00));
            list.add(new Lion("Leo III", 590.00));
            list.add(new Frog("Freddie", 7.00));

            return list;
        }

    }

他是否希望我通过同一个 ArrayList 列表从我的 Lion 类访问我的 getName() 函数?我在想我可以通过一些演员来做到这一点,就像这样:

//ArrayList<Lion> lionList = createList();
for(int j = 0; j < 3; j++){
   System.out.println(animalList.(Lion)get(j).getName());
}

但这不起作用..我不明白我应该如何在不创建另一个 ArrayList 的情况下使用我的 Lion 子类的 getName() 方法..

这是我剩下的课程:

public class Animal {
    private String genus;
    private String species;
    private double weight;
    private boolean tail;

    public Animal(){}

    public Animal(String genus, String species){
        this.genus = genus;
        this.species = species;
    }

    public String getGenus(){
        return genus;
    }

    public String getSpecies(){
        return species;
    }

    public double getWeight(){
        return weight;
    }

    public void setWeight(double weight){
        this.weight = weight;
    }

    public boolean hasTail(){
        return tail;
    }

    public void setTail(boolean tail){
        this.tail = tail;
    }

}


public class Lion extends Animal {
    private String name;

    public Lion(){}

    public Lion(String name, double weight){
        super("Panthera", "Leo");
        this.name = name;
        setWeight(weight);
        setTail(true);
    }

    public String getName(){
        return name;
    }
}


public class Frog extends Animal {
    private String name;

    public Frog(){}

    public Frog(String name, double weight){
        super("Lithobates", "Catesbeianus");
        this.name = name;
        setWeight(weight);
        setTail(false);
    }

    public String getName(){
        return name;
    }
}

【问题讨论】:

  • 青蛙也有名字吗(如“Freddie the frog”)?也许还有getName() 方法?您的老师可能会建议您将 getName() 方法移至 Animal 类。
  • 是的,Frogs 也有一个 getName() 方法。但是,说明中明确指出在 Animal 类中没有关于动物名称的任何信息..

标签: java inheritance arraylist polymorphism


【解决方案1】:

父类无法访问子类的字段或方法,因为它不知道自己实现了什么。

考虑到这一点,合乎逻辑的做法是将您需要访问的内容放在 Animal 类中。这意味着您要将name 字段拉到父Animal 类中并在那里访问它。

那么,你只需要一个ArrayList

ArrayList<Animal> animalList = createList();
for(Animal animal : animalList) {
    System.out.println(animal.getWeight());
    System.out.println(animal.getName());
}

此外,需要对您的 createList 方法进行两个非常重要的更改:

  • 不要返回原始的ArrayList。永远。您需要正确输入。

    public ArrayList<Animal> createList() { }
    
  • 您不应该关心它是 ArrayList 还是 List。最好(虽然不是必需的)编程到List的接口。

    public List<Animal> createList() { }
    

    对于奖励积分,您可以通过使其不可变或使用 PECS 来防止列表的变化:

    public List<? extends Animal> createList() { }
    

【讨论】:

  • @JakeFromStateFarm,所以,分配并没有说明您不能在 Animal 类中引用任何名称?参考您的评论:“......但是,说明中明确指出在 Animal 类中没有关于动物名称的任何信息......”
  • @nihilon 正确,作业只提到在特定动物的子类中添加名称。虽然我不明白他想让我怎么做,但我已经尝试过选角和一切。老实说,我认为他没有意识到自己的指示说了什么,哈哈。我认为 Makoto 是对的,父类对子类的字段或方法一无所知。星期一我要亲自去问我的导师。他在网上给我发了这条消息,而且很模糊。
  • 除非他想让你使用接口,否则。但这有点愚蠢,考虑到您已经有一个公共对象可供引用。
  • @JakeFromStateFarm,诚说了什么。你选择了 Makoto 的答案是对的。恕我直言,根据该指令,您可以合法地建立一个与之相矛盾的论点,并且是正确的。带有代码示例。当我看到该评论并开始尝试重新考虑它时,我已经详细建立了一个类似的答案,只是意识到如果你愿意的话,如果没有一些严肃的黑魔法师和巫毒教,这是不可能的。如果说明只在子类中提到名称,我认为走这条路不会错。不要忘记奖励积分,但请确保您理解它
  • 是的,我肯定会在这个问题上与我的导师争论,哈哈!和他交谈后,我什至可能会再次在这里发帖。无论如何,你们非常有帮助。感谢您提供有关原始返回类型和使用 List 类的信息。
【解决方案2】:

您的createList 函数返回raw-type,不要那样做。另外,我建议您对界面进行编程。但是您的第二个示例lionList 似乎需要createLionList 方法。类似的,

private static List<Lion> createLionList() {
    List<Lion> list = new ArrayList<>();
    list.add(new Lion("Leo I", 530.00));
    list.add(new Lion("Leo II", 560.00));
    list.add(new Lion("Leo III", 590.00));
    return list;
}

然后我们可以修改第一个createList来添加它。类似的,

private static List<Animal> createList() {
    List<Animal> list = new ArrayList<>();
    list.addAll(createLionList());
    list.add(new Frog("Freddie", 7.00));
    return list;
}

在您的main 方法中,我更喜欢for-each loop。和formatted output。类似的,

List<Animal> animalList = createList();
for (Animal a : animalList) {
    System.out.printf("Weight: %d lbs, Name: %s", a.getWeight(),
            a.getName());
}

【讨论】:

  • 你的意思是我的方法签名应该是 ArrayList 返回类型而不仅仅是 ArrayList?使用“List”而不是“ArrayList”是一样的吗?另外,感谢您对 foreach 循环的想法,这是有道理的,因为我只是打印所有的 Animals 而不是少数几个。
  • @JakeFromStateFarm 这基本上就是他的意思。但是使用List 作为返回类型而不是ArrayList 意味着稍后您可以将ArrayList 更改为LinkedList,或List 的其他一些实现,只需对其余代码进行最少的修改(如果有)是必要的。与其直接相关,不如说是免费提示。
  • @JakeFromStateFarm,听起来就是这样。但是,如果您已经报道了ArrayList,那么您距离报道List 也不会太远。考虑一下你的课程。 LionAnimal 的实现,与 Frog 的实现方式相同。它们都是Animals,但它们在形式和功能上更具体,因此有更具体的方法、字段等。这有意义吗?这样,ArrayListList 的实现。它在形式和功能上都更加具体,但与其他实现有共同之处。
  • @nihilon 我同意这是一种很好的做法,但我不记得上次我回去更改我最初选择的 List 实现类型是什么时候了。
  • @EricGuan,那我们的经验就不同了。我一直处于生产代码的情况,其中列表实现从ArrayList 变为LinkedList,最后在开发的前 3 个月内变为自定义实现。到自定义 imp 推出时,如果我没有习惯返回普通的旧东西,我将不得不重构 10 多个方法,以及程序中的每一个调用/分配。恕我直言,这不仅仅是一个“好习惯”。它可以直接影响预算,进而影响工作保障。我只是说。
【解决方案3】:

你的想法是对的,你只是把转换的语法弄错了。试试:

((Lion)animalList.get(j)).getName()

虽然如果青蛙和狮子都可以有名字,那么将getName() 放在Animal 类中会更有意义。

【讨论】:

  • 这似乎是对的,但它也不起作用。它说它在 Animal 类中找不到 getName()。不过,它应该在 Lion 类而不是 Animal 类中寻找这种类型,对吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-06
  • 1970-01-01
  • 2018-06-03
  • 2017-09-13
  • 2019-05-28
相关资源
最近更新 更多