ArrayList 和检索对象时的非特异性

ArrayList and non-specificity when retrieving objects

首先对这个问题表示歉意 - 我是 Java 的新手,只是想不出一个好的表达方式。

我有一个名为 "Animal" 的抽象 class 和一些继承自 "Animal" 的特定 class 称为 "Pig"、"Horse" 等.

我正在使用 ArrayList 创建一个动物列表,这些动物是实例化的猪、马等。然后我想将动物从列表中拉出并使用它们,但是 Java 将只允许我如果他们是基础 class、Animal.

,可以与他们互动

这里是我的主要代码的快速列表,可以使这一点更清楚:

import java.util.ArrayList;

public class Main {

    public static void main(String[] args) {

        ArrayList<Animal> animals = new ArrayList<Animal>();
        animals.add(new Pig("Porkey", "pink"));
        Animal animal = animals.get(0);
        animal.introAnimal(animal);
        animal.oink(); // Won't compile.Specific Pig class method   
    }
}

现在我知道我可以使用“Pig animal = (Pig) animals.get(0)”进行投射,但我的意思是我希望能够与这些动物一起工作,而不管它们是什么,并在需要时使用 getObjectType 或其他类似的东西想知道。

这似乎是多态性的障碍 :) 我不太担心这段特定的代码(我只是在练习),但更担心这个问题的一般解决方案。

非常感谢。

可以使用instanceof运算符(instanceof关键字可以用来判断一个对象是否属于指定类型。)判断animal是否指向Pig,如果它确实如此,然后执行转换并调用 Pig 的特定方法。例如

 ArrayList<Animal> animals = new ArrayList<Animal>();
        animals.add(new Pig("Porkey", "pink"));
        Animal animal = animals.get(0);
        animal.introAnimal(animal);
  if (animal instanceof Pig)            
      ((Pig)animal).oink(); // This will compile and work  

例如,如果您的 super class 包含一个方法 execute(),并且您在它的每个子类型中都实现了该函数,那么将执行正确的方法。

示例:

class pig extends Animal {
    ...
    public void execute () {
        System.out.println("Oink");
    }
    public void specificPigAction {
        ... 
   }
}

class cat extends Animal {
    ...
    public void execute () {
        System.out.println("Meow");
    }
}

现在你可以这样做了:

Animal pig = animals.get();
pig.execute(); // output = oink
Animal cat = animals.get();
cat.execute(); // output = meow

如果您希望执行可能只有猫才能执行的非常具体的任务,您必须将对象转换为正确的类型:

Pig pig = (Pig) animals.get();
pig.specificPigAction();

在后一种情况下,重要的是只有当您知道类型是正确的或将其包围在 throw catch 子句中时才进行转换。

请注意,此代码可能不符合样式规则甚至 Java 语法,但我相信这个想法很清楚。

您必须使用对动物通用的术语。您可以将 Animal 指示为 oink,而不是将其指示不适用于所有动物,而是将其指示为 speak,这可能是一个通用术语。

Java 是一种静态类型语言,因此如果您尝试使用在运行时可能无法运行的方法,编译器会报错。它不是一种允许您调用任何方法以希望它可以在运行时运行的动态语言。虽然这更灵活,但更难可靠地构建大型程序。例如说我重命名了一个方法,我怎么知道谁在调用那个方法也重命名它。

abstract class Animal {
    void speak();
}

class Pig extends Animal {
    public void speak() {
        System.out.println("Oink");
    }
}

class Dog extends Animal {
    public void speak() {
        System.out.println("Woof");
    }
}

class Duck extends Animal {
    public void speak() {
        System.out.println("Qwack");
    }
}

List<Animal> animals = Arrays.<Animal>asList(new Dog(), new Pig(), new Duck);
for (Animal animal : animals)
    animal.speak();

打印

Woof
Oink
Qwack

Thanks for your answer. The thing I was trying to get at is that I want to be able to create a list and then remove stuff from the list and be able to deal with the subclass depending on which subclass it is at run-time. I just didn't realise you could cast later on to get subclass methods.

有两种方法可以做到这一点。

List<Animal> animals = ...
for (Animal animal: animals)
    if (animal instanceof Pig) {
        ((Pig) animal).oink();
    }

这仍然需要你知道你想要一只猪。假设您有另一个 class Boar 并且它也有一个 oink 方法,并且您不关心 Pig 和 Boar 没有父对象 class(尽管你可以)

List<Animal> animals = ...
for (Animal animal: animals) {
    Method method = animal.getClass().getMethod("oink");
    if (method != null)
        method.invoke(animal);
}

现在假设该方法不仅不在 Animal 中,而且 private 对 Pig 来说更难调用。

List<Animal> animals = ...
for (Animal animal: animals) {
    Method method = animal.getClass().getDeclaredMethod("oink");
    if (method != null) {
        method.setAccessible(true);
        method.invoke(animal);
    }
}