为什么Java不允许我执行这种类型的多态/继承?

时间:2013-06-03 13:34:41

标签: 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 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的一个实例?

是否有一个特定原因导致我不允许这样做?

编辑:忘了让狗和猫延伸动物。固定的。

6 个答案:

答案 0 :(得分:5)

编译器无法保证在运行时存在适当的方法。

你有一个方法需要Cat,你有一个方法需要Dog。您正在尝试传递引用Animal的{​​{1}}变量。如果会引用Dog怎么办?然后在运行时没有合适的方法。这就是为什么它不会让你编译。

Elephant

答案 1 :(得分:3)

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

您可能也想知道为什么在大多数语言中使用单一调度...这与封装的基本思想和对象是其方法的所有者有关。考虑一下您的情况:runAction应该属于RunActions还是Animal?它同样属于两者;更好地陈述:它不属于。这带来了一个完全不同的编程模型,一个没有封装。

答案 2 :(得分:1)

首先,DogCat应该延伸Animal

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

并使用:

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

由于DogCat都是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足够聪明,当你做错时就会阻止你。希望这能清除您的疑虑并帮助您理解这一点。