首页 文章

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

提问于
浏览
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的一个实例吗?

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

EDIT: 忘了让狗和猫延伸动物 . 固定 .

6 回答

  • 3

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

    你有一个方法需要 Cat ,你有一个方法,需要 Dog . 您正在尝试传递引用 DogAnimal 变量 . 怎么会引用 Elephant ?然后在运行时没有合适的方法 . 那个's why it won' t让你编译 .

    Animal animal = new Elephant();
    new RunActions().runAction(animal); // real problem now!
    
  • 1

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

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

  • 5

    首先, 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 参数 .

  • 1

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

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

    您期望RunActions类做什么?

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


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

  • 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);
        }
    }
    
  • -1

    首先,你不是在狗和猫中扩展动物 . 所以先做 .

    继承ISA主要得到遵循 .

    所以例如

    public class Dog extends Animal
    

    在这里狗扩展动物,所以 DOG IS ANIMAL 但反过来不是真的动物不能是一只狗 . 它也可以在你的情况下 .

    因此,当您将 animal 的引用传递给接受DOG或CAT赋值的方法时,就会发生类似的情况

    Dog d=animal;
    

    这被读为 animal is 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足够聪明,可以在你做错时阻止你 . 希望这能清除您的疑虑并帮助您理解这一点 .

相关问题