web-dev-qa-db-ja.com

Javaジェネリックでインターフェースを実装する抽象クラス

Comparableを実装する抽象クラスを定義しようとしています。次の定義でクラスを定義すると:

_public abstract class MyClass implements Comparable <MyClass>
_

サブクラスはcompareTo(MyClass object)を実装する必要があります。代わりに、すべてのサブクラスでcompareTo(SubClass object)を実装し、独自のタイプのオブジェクトを受け入れます。次のような抽象クラスを定義しようとすると:

_public abstract class MyClass implements Comparable <? extends MyClass>
_

「スーパータイプはワイルドカードを指定できない」と文句を言います。

解決策はありますか?

63
Cem

私の意見では少し冗長すぎますが、うまくいきます:

public abstract class MyClass<T extends MyClass<T>> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}
43
whiskeysierra

署名を宣言する際に遭遇する機械的な問題は別として、目標はあまり意味がありません。共変比較関数を確立しようとしていますが、これは派生クラスが調整できるインターフェースを確立するという考え全体を打ち破ります。

インスタンスを他のSubClassインスタンスとのみ比較できるようにサブクラスSubClassを定義する場合、SubClassMyClassで定義されたコントラクトをどのように満たしますか? MyClassは、それとそれから派生した型を他のMyClassインスタンスと比較できると言っていることを思い出してください。 SubClassに当てはまらないようにしようとしています。つまり、SubClassMyClassの契約を満たしていません。SubClassの要件は厳しいため、MyClassSubClassに置き換えることはできません。

この問題は、共分散と反分散、およびそれらがどのように型の派生を通じて関数シグネチャを変更できるようにするかを中心にしています。 relax引数の型に対する要件(スーパータイプの署名要求よりも広い型を受け入れる)および strength戻り値の型の要件-スーパータイプの署名よりも狭い型を返すことを約束します。これらの自由のそれぞれは、スーパータイプの派生型の完全な置換を依然として許可します。呼び出し側は、スーパータイプのインターフェースを介して派生型を使用すると違いを知ることができませんが、派生型を具体的に使用する呼び出し元は、これらの自由を利用できます。

Willi's answer は一般的な宣言について何かを教えていますが、セマンティクスを犠牲にしてテクニックを受け入れる前に目標を再検討することをお勧めします。

18
seh

javaの例を参照してください。

_public abstract class Enum<E extends Enum<E>> implements Comparable<E>
    public final int compareTo(E o)
_

sehのコメント:通常、引数は正しいです。しかしジェネリックは型の関係をより複雑にします。ウィリのソリューションでは、サブクラスはMyClassのサブタイプではない可能性があります。

SubClassAは_MyClass<SubClassA>_のサブタイプですが、_MyClass<SubClassB>_のサブタイプではありません

タイプ_MyClass<X>_は、そのすべてのサブタイプが尊重しなければならないcompareTo(X)のコントラクトを定義します。そこに問題はありません。

3
irreputable

別のソリューションが見つかりました:

  1. 互換性を構成するフィールドでインターフェースを定義します(ComparableFooなど)
  2. 親クラスにインターフェースを実装する
  3. 親クラスにComparableを実装します。
  4. 実装を作成します。

ソリューションは次のようになります。

public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> {
    public int compareTo(ComparableFoo o) {
    // your implementation
    }
}

このソリューションは、より多くのものがComparableFooを実装する可能性があることを意味します-これは事実ではないかもしれませんが、インターフェイスにコーディングしているので、ジェネリック式は簡単です。

1
David Levy

キャプチャが必要かどうかわかりません:

まず、abstractクラスにcompareToを追加します...

public abstract class MyClass implements Comparable <MyClass> {

@Override
public int compareTo(MyClass c) {
...
}    
}

次に、実装を追加します...

public class MyClass1 extends MyClass {
...
}

public class MyClass2 extends MyClass {
...
}

Compareを呼び出すと、スーパータイプメソッドが呼び出されます...

MyClass1 c1 = new MyClass1();
MyClass2 c2 = new MyClass2();

c1.compareTo(c2);
1
zevra0
public abstract class MyClass<T> implements Comparable<T> {

}

public class SubClass extends MyClass<SubClass> {

    @Override
    public int compareTo(SubClass o) {
        // TODO Auto-generated method stub
        return 0;
    }

}
1
newacct

「compareTo(SubClassオブジェクト)、独自の型のオブジェクトを受け入れる」と言ったのは知っていますが、抽象クラスを次のように宣言することをお勧めします。

public abstract class MyClass implements Comparable <Object>

mySubClassでcompareToをオーバーライドするときにinstanceofチェックを実行します。

@Override
public int compareTo(Object o) {
    if (o instanceof MySubClass)) {
        ...
    }
    else throw new IllegalArgumentException(...)
}

「等しい」または「クローン」と同様

0
Caroline Even