web-dev-qa-db-ja.com

ダウンキャストを避ける方法は?

私の質問は、スーパークラスの動物の特別なケースについてです。

  1. 私のAnimalmoveForward()eat()を使用できます。
  2. SealAnimalを拡張します。
  3. DogAnimalを拡張します。
  4. また、Animalと呼ばれるHumanを拡張する特別な生き物がいます。
  5. Humanはメソッドspeak()も実装します(Animalでは実装されていません)。

Animalを受け入れる抽象メソッドの実装では、speak()メソッドを使用したいと思います。ダウンキャストをしないとそれは不可能のようです。 Jeremy Millerは 彼の記事 に、ダウンキャストの匂いがすることを書いています。

この状況でダウンキャストを回避するための解決策は何ですか?

12
Bart Weber

特定のクラスが何かをするためにHuman型であるかどうかを知る必要があるメソッドがある場合、特に SOLIDの原則 を壊しています。

  • オープン/クローズの原則-将来、話すことができる新しい動物の種類(オウムなど)を追加する必要がある場合、またはその種類に固有の何かを行う場合、既存のコードを変更する必要があります
  • インターフェース分離の原則-一般化しすぎているようです。動物は広範囲の種をカバーすることができます。

私の意見では、メソッドが特定のクラス型を期待している場合、それを特定のメソッドとして呼び出すには、そのメソッドを変更して、インターフェースではなく、そのクラスのみを受け入れるようにします。

このようなもの :

public void MakeItSpeak( Human obj );

そしてこれは好きではありません:

public void SpeakIfHuman( Animal obj );
12
BЈовић

問題は、あなたがダウンキャストしているということではなく、Humanにダウンキャストしているということです。代わりに、インターフェースを作成します。

public interface CanSpeak{
    void speak();
}

public abstract class Animal{
    //....
}

public class Human extends Animal implements CanSpeak{
    public void speak(){
        //....
    }
}

public void mysteriousMethod(Animal animal){
    //....
    if(animal instanceof CanSpeak){
        ((CanSpeak)animal).speak();
    }else{
        //Throw exception or something
    }
    //....
}

このように、条件は動物がHumanであるということではなく、話すことができるという条件です。これは、mysteriousMethodAnimalを実装している限り、CanSpeakの他の人間以外のサブクラスと連携できることを意味します。

6
Idan Arye

動物に通信を追加できます。犬の鳴き声、人間が話す、シール..うーん..私はシールが何をしているのかわかりません。

しかし、あなたのメソッドはif(Animal is Human)Speak();に設計されているようです。

あなたが聞きたい質問は、代替案は何ですか?あなたが何を成し遂げたいのか正確にはわからないので、提案するのは難しいです。ダウンキャスト/アップキャストが最善のアプローチである理論的な状況があります。

2
Andrew Hoffman

この場合、AbstractAnimalクラスのspeak()のデフォルト実装は次のようになります。

_void speak() throws CantSpeakException {
  throw new CantSpeakException();
}
_

その時点で、Abstractクラスにデフォルトの実装があり、正しく動作します。

_try {
  thingy.speak();
} catch (CantSeakException e) {
  System.out.println("You can't talk to the " + thingy.name());
}
_

はい、これはすべてのspeakを処理するためにコード全体に試行錯誤が散在していることを意味しますが、これの代わりにif(thingy is Human)がすべてのスポークをラップすることです。

例外の利点は、ある時点で話すことができる別の種類のものがあれば(オウム)、すべてのテストを再実装する必要がないことです。

2
user40980

時々ダウンキャストが必要かつ適切です。特に、何らかの能力を持つオブジェクトと持たないオブジェクトがあり、その能力のないオブジェクトをデフォルトの方法で処理しているときに、その能力が存在する場合にその能力を使用したい場合に適しています。簡単な例として、Stringが他の任意のオブジェクトと等しいかどうかを尋ねられたとします。あるStringが別のStringと等しい場合、他の文字列の長さとバッキング文字配列を調べる必要があります。ただし、StringDogと等しいかどうかを尋ねられた場合、Dogの長さにアクセスすることはできませんが、そうする必要はありません。代わりに、Stringがそれ自体と比較することになっているオブジェクトがStringではない場合、比較ではデフォルトの動作を使用する必要があります(他のオブジェクトが等しくないことを報告する)。

ダウンキャストが最も疑わしいと見なされるのは、キャストされるオブジェクトが適切なタイプであることが「わかっている」ときです。一般に、オブジェクトがCatであることがわかっている場合は、Cat型の変数ではなく、Animal型の変数を使用して参照する必要があります。ただし、これが常に機能するとは限らない場合があります。たとえば、Zooコレクションは、偶数/奇数の配列スロットにオブジェクトのペアを保持する場合があります。各ペアのオブジェクトは、それらのオブジェクトに作用できない場合でも、相互に作用できることが期待されます。他のペア。そのような場合、各ペアのオブジェクトは、特定のパラメータタイプを受け入れる必要があるため、他のペアからオブジェクトを構文的にに渡すことができます。したがって、CatplayWith(Animal other)メソッドは、otherCatである場合にのみ機能する場合でも、Zooは次のことができる必要があります。 Animal[]の要素を渡すため、そのパラメータタイプはAnimalではなくCatである必要があります。

ダウンキャストが正当に避けられない場合は、無条件で使用する必要があります。重要な問題は、ダウンキャストを賢明に回避できる時期を決定し、合理的に可能な場合にそれを回避することです。

1
supercat

動物を受け入れる抽象メソッドの実装では、speak()メソッドを使用したいと思います。

いくつかの選択肢があります。

  • 存在する場合は、リフレクションを使用してspeakを呼び出します。利点:Humanに依存しません。短所:「speak」という名前への依存関係が隠されています。

  • 新しいインターフェイスSpeakerを導入し、インターフェイスにダウンキャストします。これは、特定のコンクリートタイプに依存するよりも柔軟性があります。 Humanを実装するためにSpeakerを変更する必要があるという欠点があります。 Humanを変更できない場合、これは機能しません

  • Humanにダウンキャストします。これには、別のサブクラスに話させたいときにコードを変更する必要があるという欠点があります。理想的には、繰り返し戻って古いコードを変更することなく、コードを追加してアプリケーションを拡張する必要があります。

1
kevin cline