【问题标题】:Why doesn't Java allow me to perform this type of polymorphism/inheritance?为什么 Java 不允许我执行这种类型的多态/继承?
【发布时间】:2013-05-29 16:20:12
【问题描述】:

我正在重构一个巨大的 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 不应该在运行时找出 animal 是 Dog 的一个实例吗?

它不允许我这样做有什么具体原因吗?

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

【问题讨论】:

  • 请参阅JLS。它解释了一切。

标签: java inheritance polymorphism


【解决方案1】:

最好关注

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

【讨论】:

  • Animal 可以是一个抽象类,例如:实现所有类型Animal共享的一些共同特性。
【解决方案2】:

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

【讨论】:

  • OP 知道他可以做到这一点。他的问题是为什么不允许他发布的代码。
【解决方案3】:

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

在继承中遵循 ISA 主体。

例如

public class Dog extends Animal

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

因此,当您将 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 足够聪明,可以在你做错时阻止你。希望这能消除您的疑虑并帮助您理解这一点。

【讨论】:

  • 我认为问题是为什么。例如,CLOS 确实以 OP 在 Java 中尝试执行的方式进行调度。
  • 通用 Lisp 对象系统;它使用多个调度。
  • @DaveNewton:好的。不知道。
【解决方案4】:

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

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

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

【讨论】:

  • 你保存了我的击键。 +1
  • 这很容易理解。谢谢你。既然我们正在讨论这个问题:有什么方法可以“欺骗”编译器认为调用是可以的?例如......某种 getClass() (或其他反射的东西)来欺骗编译器认为调用会起作用?我不会那样做。我承诺。只是出于好奇。
  • @LucasTulio:不,没有。编译器查找并查看它是否找到要调用的适用方法。如果是,好的,如果不是,你的错误。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

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

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

    你希望你的 RunActions 类做什么?

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


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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-27
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多