我正在重构一个巨大的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的一个实例?
是否有一个特定原因导致我不允许这样做?
编辑:忘了让狗和猫延伸动物。固定的。
答案 0 :(得分:5)
编译器无法保证在运行时存在适当的方法。
你有一个方法需要Cat
,你有一个方法需要Dog
。您正在尝试传递引用Animal
的{{1}}变量。如果会引用Dog
怎么办?然后在运行时没有合适的方法。这就是为什么它不会让你编译。
Elephant
答案 1 :(得分:3)
使您想要的东西变得不可能的主要基本概念是Java是一种单一调度语言,就像几乎所有其他语言一样称为“OOP”。这意味着运行时决定调用哪个方法只考虑第一个方法参数,它在语法上放置在之前的,其值将绑定到this
特殊变量。
您可能也想知道为什么在大多数语言中使用单一调度...这与封装的基本思想和对象是其方法的所有者有关。考虑一下您的情况:runAction
应该属于RunActions
还是Animal
?它同样属于两者;更好地陈述:它不属于。这带来了一个完全不同的编程模型,一个没有封装。
答案 2 :(得分:1)
首先,Dog
和Cat
应该延伸Animal
:
public class Dog exttends Animal{
@Override
public void doAction() {System.out.println("run");
}
并使用:
public class RunActions {
public void runAction(Animal a) {
a.doAction();
}
}
由于Dog
和Cat
都是Animals
,您可以使用Animal
参数。
答案 3 :(得分:1)
问题不是所有动物都可能是猫或狗。考虑:
public class Fish implements Animal{
public void doAction() {System.out.println("swim");
}
您希望RunActions类做什么?
这就是编译器抱怨的原因。
您可以使用一些方法来使您的情况发挥作用。最简单的方法是使用一个接受Animal的方法,并使用一系列instanceof
测试来确定你想要对Animal的每个特定子类做什么。
答案 4 :(得分:0)
更好地关注
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);
}
}
答案 5 :(得分:-1)
首先,你不是在狗和猫中扩展动物。所以先这样做。
在继承ISA主要得到遵循。
所以例如
public class Dog extends Animal
这里狗延伸动物,以便狗是动物,但反过来不是真的动物不能是一只狗。您也可以使用它。
因此,当您将动物的引用传递给接受DOG或CAT分配的方法时,就会像
一样Dog d=animal;
被视为动物是DOG ,但事实并非如此。
因此编译器不允许你这样做。
关于为什么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足够聪明,当你做错时就会阻止你。希望这能清除您的疑虑并帮助您理解这一点。