2

我正在重构我拥有的一个巨大的 if 语句。我发现改进它的方法之一是使用多态性和继承。以一种非常简化的方式,这就是我的代码中的内容:

public abstract class Animal {  
    public abstract void doAction();  
}

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

public class Cat extends Animal {  
    public void doAction() {System.out.println("sleep");}
}

public class RunActions {  
    public void runAction(Dog d) {
        d.doAction();
    }

    public void runAction(Cat c) {
        c.doAction();
    }
}

public class Start {
    public static void main(String args[]) {
        Animal animal = new Dog();
        new RunActions().runAction(animal); // Problem!
    }
}

我知道我知道。我可以打电话给animal.doAction();。或者在 RunActions 中添加一个接收 Animal 作为参数的方法。

但是为什么编译器不允许我调用最后的“runAction(animal)”行呢?JVM 不应该在运行时找出动物是 Dog 的一个实例吗?

是否有特定原因不允许我这样做?

编辑:忘记让 Dog 和 Cat 扩展 Animal。固定的。

4

6 回答 6

5

编译器不能保证在运行时有合适的方法。

您有一个采用 a 的方法,Cat并且您有一个采用 a 的方法Dog。您正在尝试传递一个Animal引用 a 的变量Dog。如果会引用一个Elephant?那么在运行时将没有合适的方法。这就是为什么它不会让你编译。

Animal animal = new Elephant();
new RunActions().runAction(animal); // real problem now!
于 2013-06-03T13:42:04.233 回答
3

使您想要的东西变得不可能的主要基本概念是 Java 是一种单调度语言,就像几乎所有其他称为“OOP”的语言一样。这意味着运行时决定调用哪个方法只考虑第一个方法参数,它在语法上位于点之前,其值将绑定到this特殊变量。

您可能还想知道为什么在大多数语言中都使用单分派……这与封装的基本思想和对象是其方法的所有者有关。考虑你的情况:应该runAction属于RunActionsor Animal?它平等地属于两者;更好的说法是:它属于任何一个。这带来了一种完全不同的编程模型,没有封装。

于 2013-06-03T13:42:05.057 回答
1

First of all, Dog and Cat should extend Animal:

public class Dog exttends Animal{  
    @Override
    public void doAction() {System.out.println("run");  
}

And use:

public class RunActions {  
    public void runAction(Animal a) {
        a.doAction();
    }
}

As both Dog and Cat are Animals, you can use Animal argument.

于 2013-06-03T13:37:45.640 回答
1

问题不是所有的动物都可能是猫或狗。考虑:

public class Fish implements Animal{  
    public void doAction() {System.out.println("swim");  
}

你希望你的 RunActions 类做什么?

这就是编译器抱怨的原因。


您可以使用几种方法来使您的情况发挥作用。最简单的方法是拥有一个接受 Animal 的方法并使用一系列instanceof测试来确定您想对 Animal 的每个特定子类做什么。

于 2013-06-03T14:07:23.617 回答
0

Better do following

public interface Animal {  
    public void doAction();  
}

public class Dog implements Animal{  
    public void doAction() {System.out.println("run");  
}

public class Cat implements Animal{  
    public void doAction() {System.out.println("sleep");  
}

public class RunActions {  
    public void runAction(Animal d) {
        d.doAction();
    }
}

public class Start {
    public static void main(String args[]) {
        Animal animal = new Dog();
        new RunActions().runAction(animal);
    }
}
于 2013-06-03T13:36:58.463 回答
-1

首先,您没有在 Dog 和 Cat 中扩展 Animal。所以先这样做。

在继承中遵循 ISA 主体。

所以例如

public class Dog extends Animal

这里 Dog 扩展了 Animal 以便DOG IS ANIMAL但反过来不是真的 ANIMAL CANNOT BE NECESSARILY A Dog。在您的情况下,它也可以是 CAT。

因此,当您将动物的引用传递给接受 DOG 或 CAT 分配的方法时,就像

Dog d=animal;

读作动物是狗,这是不正确的。

所以编译器不允许你这样做。

至于为什么Java不允许做这件事是为了实现它能够实现的特性。

例如,Java 允许您将动物对象传递给方法并允许您执行该方法。

所以在那种情况下

Animal animal=new Dog();
Dog d= animal;
d.doSomething(); // let's say java allowed this no problem since animal is DOG.

但,

Animal animal=new Horse();
Dog d= animal;
d.doSomething(); // Can you imagine what will happen in this case?

因此,为了避免这种情况,java 足够聪明,可以在您做错时阻止您。希望这可以消除您的疑问并帮助您理解这一点。

于 2013-06-03T13:40:49.653 回答