2016-01-05 15 views
17

次の有効なユースケースは、これまであります場合、私は思ったんだけど:他のタイプと比較することは可能でしょうか?

class Base {} 

class A implements Comparable<Base> { 
    //... 
} 

どこT extends Comparable<? super T>、タイプTのコレクションを受け入れるために(例数のCollectionsを参照)一般的なパターンのようです。

しかし、基本クラスと比較するとcompareTo()の契約を履行することは技術的に不可能です。なぜなら、別のクラスが矛盾した比較でベースを拡張しないようにする方法がないからです。次の例を考えてみましょう:

class Base { 
    final int foo; 
    Base(int foo) { 
     this.foo = foo; 
    } 
} 

class A extends Base implements Comparable<Base> { 
    A(int foo) { 
     super(foo); 
    } 
    public int compareTo(Base that) { 
     return Integer.compare(this.foo, that.foo); // sort by foo ascending 
    } 
} 

class B extends Base implements Comparable<Base> { 
    B(int foo) { 
     super(foo); 
    } 
    public int compareTo(Base that) { 
     return -Integer.compare(this.foo, that.foo); // sort by foo descending 
    } 
} 

我々は(一般的なルールがあった場合、それはほぼ確実Baseに実装されるだろう)共通ルールに従わない比較を使用してBaseを拡張する2つのクラスを持っています。しかし、次の壊れた並べ替えがコンパイルされます:

Collections.sort(Arrays.asList(new A(0), new B(1))); 

はそれだけでT extends Comparable<T>を受け入れるために、より安全ではないでしょうか?または、ワイルドカードを検証するユースケースがいくつかありますか?

+0

私の腸はいいえ(リンゴやオレンジなど)はありませんが、これを考慮する可能性がある唯一の理由は、親クラスを別の方法で子クラスと比較したかったことがない場合です。もちろん、あなたはいつも 'super.compareTo'を呼び出すことができます – MadProgrammer

+0

あなたのタイトルをいくらか明確にしたいと思うかもしれません。あなたのポストで提起されたより興味深い質問は、 "T extends Comparable "ではなく、 "T extends Comparabably "で動作すると宣言されたコレクションのメソッドはなぜですか?明らかに、それは素晴らしいタイトルではありませんが、私はそのような方向性指向のタイトルが良いと思う。 – Others

+0

私はJavaの 'enum'が' Comparable'のために基本クラスを使用していることを知っています。すべてのJavaの 'enum'は' Comparable 'を実装する' java.lang.Enum'基底クラスから派生します。ここで 'E'は子クラスです(あなたが使用するenum)。 – markspace

答えて

10

これは非常に良い質問です。プロデューサーは、拡張、消費者スーパー:まずは、

binarySearch(List<? extends Comparable<? super T>> list, T key) 

などの理由Collections使用方法から始めましょう実際、このための理由だけではなく、

binarySearch(List<? extends Comparable<T>> list, T key) 

理由はPECS原則です。 binarySearchは何をしますか? 要素をリストから読み取り、と比較して、の値をcompareTo関数に渡します。要素を読み込むので、リストはプロデューサとして機能し、したがって最初の部分はプロデューサが拡張します。これは明白なので、コンシューマースーパーのパートはどうですか?

コンシューマースーパーは基本的に、ある関数に値を渡すだけの場合、オブジェクトの正確な型やそのスーパークラスを受け入れるかどうかは気にしません。だから、binarySearch宣言が言っていることは:何かがリスト要素のcompareToメソッドに渡すことができる限り私は何かを探すことができます。

ソートの場合、要素が互いに比較されるだけなので、それは明らかではありません。しかし、その場合でも、Baseが実際にComparable<Base>(と全体の比較を行います)とABをちょうど比較することなく比較してBaseに拡張すればどうなりますか? ABのリストは、それぞれComparable<A>Comparable<B>を実装していないため、並べ替えることができません。サブクラス化するたびにインターフェイス全体を再実装する必要があります。

もう1つの例:あなたのBaseを拡張していないクラスのインスタンスを含むリストに対してバイナリ検索を実行したい場合はどうなりますか?

class BaseComparable implements Comparable<Base> { 
    private final Base key; 
    // other fields 

    BaseComparable(Base key, ...) { 
     this.key = key; 
     // other fields initialization 
    } 

    @Override 
    public int compareTo(Base otherKey) { 
     return this.key.compareTo(otherKey); 
    } 
}; 

そして今、彼らはこのバイナリ検索のキーとしてAのインスタンスを使用します。彼らは? super Tの部分のためにそれを行うことができます。このクラスは、キーがABかどうかについてはわかりませんので、Comparable<A/B>を実装することはできません。

あなたの例として、私はそれが単なる貧弱なデザインの例だと思います。残念ながら、PECSの原則を破ったり、Javaジェネリックの限られた機能を制限したりすることなく、このようなことを防ぐ方法はありません。

0

制約

T extends Comparable<? super T> 

T extends S, S extends Comparable<S> 

Comparableタイプとして理解されるべきであることは、常に自己の比較です。

X <: Comparable<Y>の場合は、X <: Y <: Comparable<Y>である必要があります。

compareTo()の契約でもそれを証明することができます。逆順y.compareTo(x)を定義する必要があるため、Y <: Comparable<A>, X<:Aとなる必要があります。同じ議論の後、我々はA <: Comparable<Y>を持っています。その後、推移性はA=Yになります。

+0

契約では、技術的には 'y.compareTo(x)'が暗黙的に定義されている必要があります。 – shmosel

+0

'y.compareTo(x)'が定義されていないと、実際に契約を履行することはできません:) – ZhongYu

関連する問題