2

私が持っている巨大な if ステートメントをリファクタリングしています。それを改善するために私が見つけた方法の 1 つは、ポリモーフィズムと継承を使用することでした。非常に単純化された方法で、これは私のコードにあるものです:

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 に追加します。

しかし、コンパイラが最後の「runAction(animal)」行を呼び出すことを許可しないのはなぜですか? JVM は実行時に animal が Dog のインスタンスであると認識すべきではありませんか?

それが許可されない特定の理由はありますか?

編集: Dog と Cat を Animal 拡張にするのを忘れていました。修理済み。

4

6 に答える 6

5

コンパイラは、実行時に適切なメソッドがあることを保証できません。

を受け取るメソッドがあり、 を受け取るCatメソッドがありますDogAnimalを参照する変数を渡そうとしていますDog。を参照するとどうなりますElephantか?その場合、実行時に適切なメソッドはありません。そのため、コンパイルできません。

Animal animal = new Elephant();
new RunActions().runAction(animal); // real problem now!
于 2013-06-03T13:42:04.233 に答える
3

あなたが望むことを不可能にする主な根底にある概念は、「OOP」と呼ばれる他のほとんどすべての言語と同様に、 Java が単一ディスパッチ言語であるということです。これが意味することは、どのメソッドを呼び出すかの実行時の決定では、最初のメソッド引数のみが考慮されるということです。これは、構文的にドットの前にthis配置され、値が特殊変数にバインドされるものです。

また、ほとんどの言語で単一のディスパッチが使用される理由も不思議に思うかもしれません...これは、カプセル化とオブジェクトがメソッドの所有者であるという基本的な考え方に関係しています。あなたのケースを考えてみましょう:またはrunActionに属すべきですRunActionsAnimal?それは両方に等しく属しています。より適切に言えば、どちらにも属していません。これにより、カプセル化のない、まったく異なるプログラミング モデルが実現します。

于 2013-06-03T13:42:05.057 に答える
1

まず第一に、拡張する必要がありDogます:CatAnimal

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ます。

于 2013-06-03T13:37:45.640 に答える
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);
    }
}
于 2013-06-03T13:36:58.463 に答える
-1

まず、Dog と Cat で Animal を拡張していません。だから最初にそれをしてください。

継承では、ISA プリンシパルに従います。

たとえば

public class Dog extends Animal

ここで、Dog は Animal を拡張してDOG IS ANIMALにしますが、その逆は真ではありません ANIMAL は必ずしも DOG になることはできません。それはあなたの場合にも猫である可能性があります。

したがって、DOG または CAT 割り当てを受け入れるメソッドにanimalの参照を渡すと、次のようになります。

Dog d=animal;

動物はDOGと読みますが、そうではありません。

したがって、コンパイラはそれを許可しません。

そして、Java がそれを許可しない理由については、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 は、間違ったことをしているときに停止するのに十分なほどインテリジェントです。これがあなたの疑問を解消し、これを理解するのに役立つことを願っています.

于 2013-06-03T13:40:49.653 に答える