web-dev-qa-db-ja.com

複数の境界を持つ型パラメーターで型引数を使用できないのはなぜですか?

だから、私は理解しています それ 以下は機能しませんが なぜ 動かないの?

_interface Adapter<E> {}

class Adaptulator<I> {
    <E, A extends I & Adapter<E>> void add(Class<E> extl, Class<A> intl) {
        addAdapterFactory(new AdapterFactory<E, A>(extl, intl));
    }
}
_

add()メソッドは、コンパイルエラー、「最初のバインドが型パラメーターの場合、追加のバインドされたアダプター<E>を指定できません」(Eclipseの場合)、または「型パラメーターの後に他の境界を続けることはできません」( IDEA)で、選択してください。

明らかに、_&_の前で、タイプパラメータIを使用することは許可されていません。それだけです。 (そして、あなたが尋ねる前に、Iが具体的なクラスではないという保証がないので、それらを切り替えると機能しません。)しかし、なぜですか?アンジェリカ・ランガーのFAQを調べましたが、答えが見つかりません。

一般に、ジェネリックスの制限が恣意的であると思われる場合は、型システムが実際に正確さを強制できない状況を作成したためです。しかし、私がここでやろうとしていることをどのような場合に壊すのかはわかりません。型消去後のメソッドディスパッチと関係があるのか​​もしれませんが、add()メソッドは1つしかないので、あいまいさがないわけではありません...

誰かが私のために問題を示すことができますか?

51
David Moles

なぜ制限があるのか​​もわかりません。 Java 5 Generics(主にGiladBrachaとNealGafter)の設計者にわかりやすい電子メールを送信してみてください。

私の推測では、言語を必要以上に複雑にしないために、彼らは 交差型 (これは本質的に複数の境界が何であるか)の絶対最小値のみをサポートしたかったと思います。交差は型注釈として使用できません。プログラマーは、交差が型変数の上限として表示される場合にのみ、交差を表現できます。

そして、なぜこのケースはサポートされたのですか?答えは、複数の境界を使用すると消去を制御できるため、既存のクラスを生成するときにバイナリ互換性を維持できるということです。 NaftalinとWadlerによる book のセクション17.4で説明されているように、maxメソッドは論理的に次の署名を持ちます。

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll)

ただし、これは次のように消去されます。

public static Comparable max(Collection coll)

これはmaxの履歴署名と一致せず、古いクライアントを破壊します。複数の境界がある場合、消去の対象となるのは左端の境界のみであるため、maxに次の署名が与えられている場合:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

次に、その署名の消去は次のようになります。

public static Object max(Collection coll)

これは、ジェネリックスの前のmaxの署名と同じです。

Java設計者は、この単純なケースのみを気にし、交差型のその他の(より高度な)使用を制限したのは、それがもたらす可能性のある複雑さを確信していなかったためであると考えられます。この設計上の決定は、(質問が示唆するように)考えられる安全上の問題である必要はありません。

今後のOOPSLAペーパー での交差型とジェネリックスの制限に関する詳細な説明。

28
Bruno De Fraine

これを非合法化する2つの考えられる理由:

  1. 複雑。 Sun bug 4899305 は、型パラメーターと追加のパラメーター化された型を含む境界が、既存よりもさらに複雑な相互再帰型を可能にすることを示唆しています。要するに、 ブルーノの答え

  2. 不正なタイプを指定する可能性。具体的には、 異なるパラメーターを使用して汎用インターフェースを2回拡張する 。工夫されていない例を思いつくことはできませんが、次のようになります。

    / **指定されたタイプTも実装するComparator <String>が含まれます。*/
     class StringComparatorHolder <T、C extends T&Comparator <String >> {
    プライベートファイナルCコンパレータ; 
     // ... 
    } 
     
     void foo(StringComparatorHolder <Comparator <Integer>、?> holder){...}

holder.comparatorComparator<Integer>Comparator<String>。これがコンパイラにどれほどの問題を引き起こすかは私にはわかりませんが、明らかに良くありません。特に、Comparatorに次のようなメソッドがあるとします。

void sort(List <?extends T> list);

私たちのComparator<Integer>/Comparator<String>ハイブリッドには、同じ消去の2つのメソッドがあります。

void sort(List <?extends Integer> list); 
 void sort(List <?extends String> list);

このようなタイプを直接指定できないのは、次のような理由によるものです。

<T extends Comparator <Integer>&Comparator <String >> void bar(){...}
Java.util.Comparatorは、異なる引数で継承することはできません:
 <Java.lang.Integer>および<Java.lang.String>

<A extends I & Adapter<E>>は、同じことを間接的に行うことを可能にします。

14
Chris Povirk

[〜#〜] jls [〜#〜] からの別の引用です:

境界の形式は制限されており(最初の要素のみがクラス変数または型変数であり、1つの型変数のみが境界に表示される場合があります)特定の厄介な状況が発生するのを防ぎます

それらの厄介な状況は正確には何ですか、私は知りません。

11
Jan Soltis

これはおそらく根本的な質問には答えませんが、仕様が明確にそれを禁止していることを指摘したいだけです。エラーメッセージをGoogleで検索すると、 このブログエントリ に移動しました。これはさらに jls 4.4 を指します。

境界は、型変数、またはクラスまたはインターフェイスタイプTのいずれかで構成され、その後にさらにインターフェイスタイプI1、...、Inが続く場合があります。

したがって、typeパラメーターをバウンドとして使用する場合、エラーメッセージに示されているように、他のバウンドを使用することはできません。

なぜ制限なのか?何も思いつきません。

2