为什么';Java允许我执行这种类型的多态性/继承吗?
我正在重构一个巨大的if语句。我发现改进它的方法之一是使用多态性和继承。以一种非常简单的方式,这就是我的代码中的内容:为什么';Java允许我执行这种类型的多态性/继承吗?,java,inheritance,polymorphism,Java,Inheritance,Polymorphism,我正在重构一个巨大的if语句。我发现改进它的方法之一是使用多态性和继承。以一种非常简单的方式,这就是我的代码中的内容: public abstract class Animal { public abstract void doAction(); } public class Dog extends Animal { public void doAction() {System.out.println("run");} } public class Cat ex
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不应该知道animal在运行时是Dog的一个实例吗
有没有具体的原因不允许我这么做
编辑:忘记让狗和猫扩展动物。已修复。最好执行以下操作
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);
}
}
首先,
狗
和猫
应该扩展动物
:
public class Dog exttends Animal{
@Override
public void doAction() {System.out.println("run");
}
和使用:
public class RunActions {
public void runAction(Animal a) {
a.doAction();
}
}
由于
狗
和猫
都是动物
,您可以使用动物
参数。首先,您没有在狗和猫中扩展动物。所以先这样做
在继承中,遵循ISA主体
比如说
public class Dog extends Animal
在这里,狗扩展了动物,所以狗就是动物,但反过来说,动物不一定是狗。在你的情况下,它也可能是猫
因此,当您将动物的引用传递给接受狗或猫分配的方法时
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.
但是,
因此,为了避免这种情况,java足够智能,可以在您做错事情时阻止您。希望这能消除您的疑虑并帮助您理解这一点。编译器不能保证在运行时有合适的方法 您有一个方法,它接受一只
猫
,您有一个方法,它接受一只狗
。您正试图传递引用狗的动物变量。如果引用一头大象
,该怎么办?那么在运行时就没有合适的方法了。这就是为什么它不允许您编译
Animal animal = new Elephant();
new RunActions().runAction(animal); // real problem now!
让您想要的东西变得不可能的主要基本概念是Java是一种单一的调度语言,就像几乎所有其他被称为“OOP”的语言一样。这意味着,运行时决定调用哪个方法只考虑第一个方法参数,该参数在语法上位于点之前,其值将绑定到这个
特殊变量
您可能还想知道为什么在大多数语言中使用单一分派。。。这与封装的基本思想有关,对象是其方法的所有者。考虑你的情况:<代码> RunActudio<代码>属于<代码>运行> <代码>还是<代码>动物< /代码>?它平等地属于两者;更好的说法是:它不属于任何一方。这带来了一个完全不同的编程模型,一个没有封装的模型。问题是并非所有动物都是猫或狗。考虑:
public class Fish implements Animal{
public void doAction() {System.out.println("swim");
}
您希望您的RunActions类做什么
这就是编译器抱怨的原因
有几种方法可以让你的情况发挥作用。最简单的方法是有一个接受Animal的方法,并使用一系列的instanceof
测试来确定你想对Animal的每个特定子类做什么。Animal可以是一个抽象类,例如实现所有类型的Animal
共享的一些公共特性。不过,我认为问题是为什么。例如,CLOS以OP在Java.Common Lisp对象系统中尝试的方式进行调度;它使用多个分派。@DaveNewton:好的。不知道。请参阅。它解释了一切。你救了我的击键+1这很容易理解。非常感谢。既然我们在讨论这个问题:有没有办法“欺骗”编译器,使其认为调用是可以的?例如某种getClass()(或其他反射内容)来欺骗编译器,使其认为调用可以工作?我不会那么做的。我保证。这只是出于好奇。@LucasTulio:不,没有。编译器查看并查看是否找到要调用的适用方法。如果是,好的,如果不是,你的错误。OP知道他可能会这样做。他的问题是为什么不允许他发布代码。
public class Fish implements Animal{
public void doAction() {System.out.println("swim");
}