81

Java のジェネリック型には、あらゆる種類の直観に反するプロパティがあることを私は知っています。これは特に私が理解していないものであり、誰かが私に説明してくれることを望んでいます. クラスまたはインターフェイスの型パラメーターを指定する場合、複数のインターフェイスを実装する必要があるようにバインドできますpublic class Foo<T extends InterfaceA & InterfaceB>。ただし、実際のオブジェクトをインスタンス化する場合、これは機能しなくなります。List<? extends InterfaceA>問題ありList<? extends InterfaceA & InterfaceB>ませんが、コンパイルに失敗します。次の完全なスニペットを検討してください。

import java.util.List;

public class Test {

  static interface A {
    public int getSomething();
  }

  static interface B {
    public int getSomethingElse();
  }

  static class AandB implements A, B {
    public int getSomething() { return 1; }
    public int getSomethingElse() { return 2; }
  }

  // Notice the multiple bounds here. This works.
  static class AandBList<T extends A & B> {
    List<T> list;

    public List<T> getList() { return list; }
  }

  public static void main(String [] args) {
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    // This last one fails to compile!
    List<? extends A & B> foobar = new LinkedList<AandB>();
  }
}

のセマンティクスはbar明確に定義されている必要があるようです。1 つの型だけでなく 2 つの型の共通部分を許可することで、型の安全性が失われるとは考えられません。確かに説明はあるけど。誰がそれが何であるか知っていますか?

4

5 に答える 5

43

興味深いことに、インターフェイスjava.lang.reflect.WildcardTypeはワイルドカード引数の上限と下限の両方をサポートしているように見えます。それぞれに複数の境界を含めることができます

Type[] getUpperBounds();
Type[] getLowerBounds();

これは、言語が許容する範囲をはるかに超えています。ソースコードに隠しコメントがあります

// one or many? Up to language spec; currently only one, but this API
// allows for generalization.

インターフェイスの作成者は、これは偶発的な制限であると考えているようです。

あなたの質問に対する定型的な答えは、ジェネリックはそのままではすでに複雑すぎるということです。複雑さを追加すると、最後のストローになる可能性があります。

ワイルドカードに複数の上限を設定できるようにするには、仕様をスキャンして、システム全体が引き続き機能することを確認する必要があります。

私が知っている問題の 1 つは、型の推論です。現在の推論規則は単純に交差タイプを処理できません。制約を減らすルールはありませんA&B << C。に減らしたら

    A<<C 
  or
    A<<B

現在の推論エンジンは、このような分岐を可能にするために大規模なオーバーホールを行う必要があります。しかし、本当の深刻な問題は、これにより複数のソリューションが可能になることですが、いずれかを優先する正当な理由はありません。

ただし、型の安全性にとって推論は必須ではありません。この場合、単純に推論を拒否し、型引数を明示的に入力するようにプログラマーに依頼することができます。したがって、推論の難しさは交差型に対する強い議論ではありません。

于 2011-07-11T03:19:19.593 に答える
27

Java言語仕様から:

4.9 交差タイプ 交差タイプは、T1 & ... & Tn, n>0 の形式を取ります。ここで、Ti, 1in はタイプ式です。交差型は、キャプチャ変換 (§5.1.10) と型推論 (§15.12.2.7) のプロセスで発生します。プログラムの一部として交差タイプを直接記述することはできません。これをサポートする構文はありません。交差タイプの値は、すべてのタイプ Ti の値であるオブジェクトです。

では、なぜこれがサポートされていないのでしょうか。私の推測では、あなたはそのようなことをどうするべきですか?- それが可能だったとしましょう:

List<? extends A & B> list = ...

じゃあどうすればいいの

list.get(0);

戻る?の戻り値をキャプチャする構文はありませんA & B。そのようなリストに何かを追加することもできないため、基本的には役に立ちません。

于 2011-07-10T20:15:29.030 に答える
12

問題ありません...メソッドシグネチャで必要なタイプを宣言するだけです。

これはコンパイルします:

public static <T extends A & B> void main(String[] args) throws Exception
{
    AandBList<AandB> foo = new AandBList<AandB>(); // This works fine!
    foo.getList().add(new AandB());
    List<? extends A> bar = new LinkedList<AandB>(); // This is fine too
    List<T> foobar = new LinkedList<T>(); // This compiles!
}
于 2011-07-10T21:05:01.880 に答える
2

良い質問。理解するのにしばらく時間がかかりました。

ケースを単純化しましょう: 2 つのインターフェースを拡張するクラスを宣言し、次にそれらの 2 つのインターフェースを型として持つ変数を宣言する場合と同じことをしようとしています。

  class MyClass implements Int1, Int2 { }

  Int1 & Int2 variable = new MyClass()

もちろん違法。これは、ジェネリックでやろうとしていることと同じです。あなたがやろうとしていることは次のとおりです。

  List<? extends A & B> foobar;

しかし、foobar を使用するには、次のように両方のインターフェースの変数を使用する必要があります

  A & B element = foobar.get(0);

これはJavaでは合法ではありません。これは、リストの要素を同時に 2 つの型として宣言していることを意味し、たとえ私たちの脳がそれを処理できたとしても、Java 言語は処理できません。

于 2011-07-10T20:12:52.930 に答える
-1

価値があるのは、実際にこれを実際に使用したいという理由で疑問に思っている人がいれば、私が作業しているすべてのインターフェイスとクラスのすべてのメソッドの結合を含むインターフェイスを定義することで、この問題を回避したことです。つまり、私は次のことをしようとしていました:

class A {}

interface B {}

List<? extends A & B> list;

これは違法です-代わりにこれを行いました:

class A {
  <A methods>
}

interface B {
  <B methods>
}

interface C {
  <A methods>
  <B methods>
}

List<C> list;

これは、何かを のように入力できるほど便利ではありませんList<? extends A implements B>。たとえば、誰かが A または B にメソッドを追加または削除した場合、リストの入力は自動的に更新されず、手動で C を変更する必要があります。私のニーズ。

于 2017-04-10T23:15:32.273 に答える