10

JavaジェネリックのフォローアップとしてEclipseでコンパイルしますが、javacではコンパイルしないため、Eclipseで正常にコンパイルおよび実行されるが、javacでコンパイルエラーが発生する別のスニペットを投稿します。(これにより、スニペットが抽出されたプロジェクトがMavenでビルドされなくなります。)

自己完結型のスニペット:

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class Main {
  public static void main(String[] args) {
    Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
    List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
  }


  public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c) {
    List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
  }


  public static class Foo<T> implements Comparable<Foo<T>> {
    @Override
    public int compareTo(Foo<T> o) {
      return 0;
    }
  }
}

javacでのコンパイルは以下を返します:

Main.java:11: <T>asSortedList(java.util.Collection<T>) in Main cannot be applied to (java.util.Set<Main.Foo<?>>)
    List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
                                    ^

上記のスニペットに置き換えるとFoo<?>Foo<String>javacでコンパイルされます。これは、問題が使用されているワイルドカードに関連していることを意味します。Eclipseコンパイラはより耐性があると思われるので、スニペットが有効なJavaではない可能性はありますか?

(私はjavac1.6.0_37とEclipseIndigoをコンパイラコンプライアンスレベル1.6で使用しています)

EDIT1:EDIT2で削除された別の例が含まれています。)

EDIT2 :評判が悪く、比較することは概念的に間違っている可能性があり、sehの答えに触発されてFoo<A>、作業次のように書くことができます。Foo<B>asSortedFooList

public static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
    List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
}

Comparable<T>(上記のメソッド定義でのwithの単純な置換Foo<?>。)したがって、javacとimhoは、概念的にanyFoo<A>とを比較するのが安全であるように思われますFoo<B>asSortedListただし、その型引数がワイルドカードでパラメーター化されている場合、ジェネリックコレクションのソートされたリスト表現を返すジェネリックメソッドを作成することはまだできません。Foo<?>で置換してjavacを「だまそう」としS extends Comparable<S>ましたasSortedFooListが、これは機能しませんでした。

EDIT3:後でラファエルComparable<Foo<T>>は、実装は必要ないので設計に欠陥があることを指摘し、実装Comparable<Foo<?>>は同じ機能を提供し、洗練された設計によって最初の問題を解決します。

(最初の理由と利点は、aがその具体的なタイプFoo<T>を気にしないかもしれないが、それでも具体的なタイプのインスタンスを使用する可能性があることでしTた。他の目的のためにインスタンス化されます。そのインスタンスは、を決定するために使用する必要はありません。FooAPIの他の部分で使用される可能性があるため、他のsの中で順序付けします。

具体的な例:各Fooが。の異なる型引数でインスタンス化されると仮定しますT。のすべてのインスタンスには、 -methodの実装で使用されるFoo<T>タイプの増分IDがあります。これで、これらの異なる型のリストを並べ替えることができ、具象型を気にせず(で表現)、後で処理するために具象型のインスタンスにアクセスできます。)intcompareToFooTFoo<?>T

4

5 に答える 5

2

この場合、javacは正しいです。概念的には、セットにとが含まれている可能性があるため、コードは機能しませんFoo<A>Foo<B>これらは相互に比較できません。

おそらく、Set<Foo<X>>ある型変数Xのセットをaにする必要があります。残念ながら、メソッド本体内に型変数を導入することはできません。メソッドシグネチャのみ

<X> void test(){
    Set<Foo<X>> setOfFoos = new HashSet<Foo<X>>();
    List<Foo<X>> sortedListOfFoos = asSortedList(setOfFoos);
}

あなたはそれを次のようなもので機能させることができます

<T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c) 


class Foo<T> implements Comparable<Foo<?>> 
于 2012-11-23T21:17:13.393 に答える
2

私にとって、これは別のjavacバグです。Collection<Foo<?>>シグニチャを使用してメソッドにを送信しようとすると、次のようになります。

public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c)

コンパイラーは、仮パラメーター Tに上限があることに注意するため、制約が呼び出し元によって尊重されているかどうかを確認します。type引数は、パラメーター化された型の(ワイルドカード)インスタンス化であるため、 is-aのFoo<T>場合、テストは合格します。一般的な定義に基づく:Foo<?> Comparable<Foo<?>>

class Foo<T> implements Comparable<Foo<T>>

それは本当だと思いますので、やはりEclipseは正しくjavac、バグがあります。このアンジェリカ・ランガーのエントリーは決して十分にリンクされていません。関連するJLSも参照してください。

あなたはそれがタイプセーフかどうか尋ねました。私の答えは、それはタイプセーフであり、デザインに欠陥があることを示しているということです。インターフェイスの架空の実装について考えてみましょう。Comparable<T>ここでは、さらに2つのフィールドを追加しました。

public static class Foo<T> implements Comparable<Foo<T>> {

  private T pState;
  private String state;

  @Override
  public int compareTo(Foo<T> other) {
    return 0;
  }
}

あなたはいつも戻ってくる0ので、問題は発見されません。しかし、それを便利にしようとすると、2つの選択肢があります。

  1. 文字列フィールドでの比較
  2. Tメンバーの比較

Stringフィールドは常にであるため、型変数のString恩恵を受けることはあまりありませんT。一方、T他のタイプ情報は利用compareTo()できないため、ではプレーンオブジェクトしか処理できず、タイプパラメータは役に立ちません。実装することで、まったく同じ機能を実現できますComparable<Foo<?>>

于 2012-11-24T16:48:52.960 に答える
1

これが質問かどうかはわかりませんが、ここに(あまり良くない)答えがあります:型安全性を犠牲にすれば、書くことができます

@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends Comparable> List<T> asSortedList(Collection<T> c) {
    List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
}

そして、それはeclipseとjavacの両方で機能します。私が知っている唯一のリスクは、誰かが作成したclass Foo extends Comparable<Bazz>場合、コンパイル時にそれを検出できないことです。しかし、誰かが作成Foo extends Comparable<Bazz>した場合は、彼/彼女を殺してください。

于 2012-11-23T20:39:48.363 に答える
1

javacでコンパイルするソリューションを見つけましたが、それが機能する理由を正確に説明できないことに満足していません。仲介機能を導入する必要があります。

public final class Main {
  public static class Foo<T> implements Comparable<Foo<T>> {
    @Override
    public int compareTo(Foo<T> o) {
      return 0;
    }
  }


  public static <T extends Comparable<? super T>>
  List<T> asSortedList(Collection<T> c) {
    final List<T> list = new ArrayList<T>(c);
    java.util.Collections.sort(list);
    return list;
  }


  private static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
    return asSortedList(c);
  }


  public static void main(String[] args) {
    final Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
    final List<Foo<?>> listOfFoos = asSortedFooList(setOfFoos);
  }
}

これは、ワイルドカードの解決を段階的に行うことで機能すると思います。のタイプパラメータに関係なく、でasSortedFooList()あることがわかっている1つのタイプをキャプチャします。その型パラメーターがでバインドされた状態で、元の(1つの変更を加えて-の型パラメーターの下限に注意してください)を呼び出して、から派生した型としてバインドする必要があります。FooFooasSortedFooList()asSortedList()ComparableFooComparable

繰り返しますが、それは弱く、無計画な説明です。ここで答える私の主なポイントは、目的地に到達するためのもう1つの方法を提供することです。

于 2012-11-23T21:16:12.363 に答える
0

ワイルドカードの使用法を正確なタイプ(スーパータイプの場合もあります)に置き換えることができれば、コードは機能します。交換

List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);

List<Foo<String>> sortedListOfFoos = Main.<Foo<String>>asSortedList(setOfFoos);
于 2012-11-23T20:33:01.680 に答える