57

では、以下がうまくいかないことは理解していますが、なぜうまくいかないのでしょうか?

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()メソッドは、「最初のバインドが型パラメーターである場合、追加のバインドされた Adapter<E> を指定できません」(Eclipse の場合)、または「型パラメーターの後に他の境界を続けることはできません」(IDEA の場合) というコンパイル エラーを返します。選択してください。 .

I明らかに、 の前にtype パラメータを使用することは許可されていません。それだけです&。(質問する前に言っておきますが、具体的なクラスではないという保証はないので、それらを切り替えても機能しませんI。) しかし、なぜでしょうか? Angelika Langer の FAQ を調べましたが、答えが見つかりません。

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

誰かが私のために問題を実証できますか?

4

5 に答える 5

35

また、なぜ制限があるのか​​ もわかりません。Java 5 Generics の設計者 (主に Gilad Bracha と Neal Gafter) に親切な電子メールを送ってみてください。

私の推測では、言語が必要以上に複雑にならないようにするために、絶対に最小限の交差タイプ(複数の境界とは本質的に同じもの) のみをサポートしたかったのでしょう。共通部分は型注釈として使用できません。プログラマは、交差が型変数の上限として現れる場合にのみ交差を表現できます。

そして、なぜこのケースが支持されたのですか?答えは、複数の境界を使用すると消去を制御できるため、既存のクラスを生成するときにバイナリ互換性を維持できるからです。Naftalin と Wadler によるのセクション 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 ペーパー を参照してください。

于 2008-10-13T12:25:15.343 に答える
16

これを非合法化する理由として考えられるのは、次の 2 つです。

  1. 複雑。 JDK-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>>間接的に同じことができるので、それもアウトです。

于 2008-10-17T03:16:31.610 に答える
12

JLSからの別の引用は次のとおりです。

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

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

于 2008-10-13T15:55:25.183 に答える
2

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

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

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

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

于 2008-10-13T11:33:49.737 に答える