2016-07-24 16 views
7

compare()関数を持つParentインターフェイスがあるとします。Javaポリモーフィズム:入力パラメータの型変換を避けるにはどうすればよいですか?

public interface Parent { 
    public int compare(Parent otherParent); 
} 

子供がCHILD1、CHILD2、Child3はまた、このインターフェイスの親

public class Child1 implements Parent { 
    @Override 
    public int compare(Parent other) { 
     Child1 otherChild = (Child1)other; 
    } 
} 

を実装すると仮定し、私は他のどこでも、コードでジェネリック<T extends Parent>を使用しています。したがって、タイプTの2つのオブジェクトを他のコード部分と比較する必要があります。

私は比較()関数の中で親オブジェクトを型キャストしていると私も入力タイプCHILD1であるかどうかわからないとして、これは悪いデザインであることを理解。

ジェネリックの使用中にこのタイプキャストを避けるにはどうすればよいですか?

答えて

2

できません。 Javaインタフェースは、名前が意味するものと全く同じものです。受け入れられる型を指定するインタフェースです。したがって、正確には、のインターフェイスで定義されているコンパイル時と同じメソッドシグネチャが必要です。

はい、子供のタイプにこのバックをキャストすることは悪いデザインのように感じている - それは実際にJavaが決まり悪いデザインですので、それはあなたのせいではありません。

+0

推測は、型キャスト、次に使用します。ありがとう! –

3

答えは、あなたの継承階層どのように扱うかによって異なります。

  • 派生クラスで唯一、自分のクラスのインスタンスを比較しなければならない場合は、別のクラスのオブジェクトを提示したときにエラーをスローすることが許可されています、派生クラスは、タイプの境界を越えて比較しなければならない場合は、キャストを使用していますが、instanceof
  • をチェックして、それを保護する、より複雑な戦略が鋳造を回避するために必要とされて:あなたは比較を実行するために必要な二重派遣を実装するために、訪問者のようなパターンを使用することができます。ここで

抽象クラスを使用してサンプル実装です:このアプローチでは

// Use this class as the base class of your Child classes 
class AbstractChild implements Parent { 
    @Override 
    public int compare(Parent otherObj) { 
     if (!()) { 
      throw new IllegalStateException("Unexpected implementation of Child"); 
     } 
     AbstractChild other = (AbstractChild)otherObj; 
     return doCompare(other); 
    } 
    protected abstract int doCompare(AbstractChild other); 
    protected abstract int accept(Child1 c1); 
    protected abstract int accept(Child2 c2); 
    protected abstract int accept(Child3 c3); 
} 
class Child1 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 
class Child2 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 
class Child3 extends AbstractChild { 
    @Override 
    protected int doCompare(AbstractChild other) { 
     return other.accept(this); 
    } 
    @Override 
    protected int accept(Child1 c1) { 
     // Compare c1 instance to this object 
    } 
    @Override 
    protected int accept(Child2 c2) { 
     // Compare c2 instance to this object 
    } 
    @Override 
    protected int accept(Child3 c3) { 
     // Compare c3 instance to this object 
    } 
} 

唯一のキャストは、抽象クラスレベルです。実際の比較ロジックの実装は、acceptメソッドに含まれています。比較は、既知のタイプの2つのオブジェクト間で行われます。

+0

私はこのコードをin-method型のcastin/checkingと比較した場合のメリットについては少し犠牲になっています。私は過去10年間、Javaの専門家の過半数を務めていませんでした(Java 1.4はいつから出ていましたか?)。そのため、実際のパフォーマンスメリットに合った適切な方法に「委任」するためにJVMにタイプの比較をさせる(強く型付けされた言語が型の曖昧さ回避を扱うことを可能にするのとは別に、おそらく「適切なこと」であることを除いて)他の利点がありますか? –

+0

@MarcusMüllerこれは何らかの理由でそれを避けたいと思う「どのようにキャストを避けるのか」という質問に対する答えです。私は過去のような非常に特殊な状況で、訪問者を比較以上のものに利用できるようなアプローチを使用しました。私はJava-5が出てきた頃にJavaからC#に移行しました。私はそこに複数のディスパッチを実装するために別の言語メカニズムを使用しました。(つまり、C#4以降で利用可能な '動的')。 – dasblinkenlight

+0

私のコメントは少し話題外であることに同意します:)明らかに、訪問者のパターンは、さまざまな型情報オブジェクトをきれいに扱うには限られている多態的言語で物事を渡すことによって失われた型情報を回復する古典的アプローチですJavaとC#の比較やPythonのような動的な型付けされたスクリプト言語でさえ、実際に型をcallableのdictのキーとして使うことができます。私の希望は、あなたがこのことについて少し経験してくれたことです。感謝しました! –

2

一般的なケースでは避けることはできません。子クラスの数が固定されている場合は、あなたはより多くのコードの書き込みを犠牲にして鋳造を避けるために、ビジターパターンに似た何かを適用することができます。これは、キャストを避けるん

interface Parent { 
    int compare(Parent p); 
    protected int compareWith(Child1 c); 
    protected int compareWith(Child2 c); 
    protected int compareWith(Child3 c); 
} 

class Child1 implements Parent { 
    @Override int compare(Parent p) { 
     return p.compareWith(this); 
    } 
    @Override int compareWith(Child1 c) { 
     //specific code to child1 
    } 
    @Override int compareWith(Child2 c) { 
     //specific code to child2 
    } 
    @Override int compareWith(Child3 c) { 
     //specific code to child3 
    } 
} 
// and so on for the other children 

を、私はよく分かりませんあなたがここに提示した場合には、余計な努力が必要です。

5

これはなぜですか?

interface Parent<T extends Parent<?>> { 
    int compare(T type); 
} 

class Child1 implements Parent<Child1>{ 

    @Override 
    public int compare(Child1 type) { 
     return 0; 
    } 
} 

編集:あなたが

interface Parent<T extends Parent<T>>{ /* ... */ } //instead of wildcard 

使用することができます。しかし、「ループ」はかなり見ていないことを正直に言うと正しい使用方法を確実にするために、およびジェネリック以来、Javaで実行時には動作しません。 (more information)、彼らはあなたが "悪いデザイン"と呼んだ同じキャストのために本質的に統語的な砂糖なので、私はあなたの現在のアプローチが悪いとは思わない。

+1

'extend Parent '部分は実際には見えません。しかし、実際には、実装者が適切な 'T'を使用することを前提としています。これは 'Comparable'でも同じです。 – Marco13

2

正確にはパターンはComparableです。実際には、Parentインターフェイスを省略し、代わりにComparableに置き換えることを検討する必要があります。タイプは、パラメータとして与えることができ、唯一のマッチタイプはcompareメソッドに渡すことができることを確認する:

interface Parent<T> { 
    int compare(T that); 
} 

class Child1 implements Parent<Child1> { 
    @Override 
    public int compare(Child1 that) { 
     ... 
    } 
} 

Comparableインタフェースとその用途もこれを解決する方法を示しその他の場合には、少なくとも異なるChild -classesを互いに比較した場合にどうするかを考えなければならないだろう:私は持っているだろう

Child1 c1 = new Child1(); 
Child2 c2 = new Child2(); 

// Should this be possible? What should happen here? 
// How should different child classes be compared? 
c1.compare(c2); 
関連する問題