14

私は最近、Java メソッドを呼び出すときにジェネリック型を明示的に宣言するための奇妙な構文に出くわしました。例えば:

Collections.<String>emptyList();

空の を返しますList<String><T> emptyList()ただし、 の実装はチェックされていない型キャストであるため、これはばかげているように見え(List<T>) EMPTY_LISTます。そのため、すべての結果は同じ型の消去になります (そして同じオブジェクトになります)。さらに、この種の明示的な型宣言は通常必要ありません。タイプを推測します。

List<String> empty = Collections.emptyList();

さらに掘り下げた後、この構文を使用したい場所が他に2つ見つかりました。それらはすべて、グアバライブラリを使用しており、1行にあまりにも多くのステートメントを配置しようとしていることが原因です。

  1. 同期ラッパーなどを使用してコレクションをデコレートし、コンパイラが型を推測できない。型宣言を取り出すと、次は機能しません: cannot convert from Set<Object> to Set<String>:

    Set<String> set = Collections.synchronizedSet(Sets.<String>newHashSet());
    
  2. コンパイラが具体的すぎる型パラメーターを作成しようとすると、具体的でない型パラメーターを取得します。たとえば、型宣言がないと、次のステートメントも同様に文句を言います: cannot convert from Map<String, String> to Map<String, Object>:

    Map<String, Object> toJson = ImmutableMap.<String, Object>of("foo", "bar");
    

最初のケースでは推論された型パラメーターが一般的すぎ、2 番目のケースでは具体的すぎるというのは皮肉なことですが、それは Java のジェネリック システムの成果物に過ぎないと思います。

ただし、この言語構​​造自体は、Guava チームによって発明されたこれらの奇妙な使用例を除いて回避できるようです。さらに、コンパイラが上記の両方の例で型引数を推論する方法があり開発者がそうしないことを選択したことは明らかです。Javaプログラミングでこの構造を使用することが必要または有用であるという例はありますか?それとも、コンパイラを単純にする/JDK開発者の生活を楽にするためだけに存在しますか?

4

3 に答える 3

5

「コンパイラをシャットダウンする」ことが「必要または有用」ではないのはなぜですか? コードをコンパイルするために必要であり、有用でもあります。

すでにわかっているように、正しい型を推測できない場合があります。このような場合、型パラメータを明示的に指定する必要があります。コンパイラが十分に賢くない例:

型推論の複雑さを深く掘り下げたい場合は、Java 言語仕様で始まり、終わりますJLS §15.12.2.7に注目してください。実際の引数および§15.12.2.8に基づく型引数の推論。未解決の型引数の推論.

于 2013-03-26T04:55:39.830 に答える
1

コンパイラが型を正しく推論するケースが少なくとも 1 つ見つかりましたが、それはまだ必要です: 結果をより一般的な型として使用したい場合です。基本的にList<T>0 個以上のTオブジェクトから を作成するこのメソッドを使用します。

public static <T> List<T> listOf(T... items) {
    ArrayList<T> list = new ArrayList<T>();
    for (T item : items)
        list.add(item);

    return list;
}

アイデアは、次のように使用できるということです。

List<Integer> numbers = ListUtils.listOf(1, 2, 3);

今、受け取ることができるメソッドがあるとしますList<Object>:

public static void a(List<Object> objs) {
    ...
}

そして、メソッドを介して構築されたリストを提供したいlistOf()

a(ListUtils.listOf(1, 2, 3));

List<Object>メソッド パラメータの型がであり、指定された引数が であるため、これはコンパイルされませんList<Integer>。その場合、呼び出しを次のように変更できます。

a(ListUtils.<Object>listOf(1, 2, 3));

期待どおりにコンパイルされます。

于 2014-08-27T19:38:08.203 に答える
0

Java の型推論は非常に弱いものです。ジェネリック メソッドに明示的な型を含める必要がないemptyList()のは、メソッドの結果が変数を定義する場合だけです。空のリストを別のメソッドの引数として渡そうとすると (例 1)、日常的に発生する状況 (そして私はまだ Guava を使用していません) では、コンパイラは型推論を完全に放棄します。空のリストをローカルの使い捨て変数として宣言すると、「1行にあまりにも多くのステートメントを配置する」ことがどのように行われるかわかりません。空のリストは非常に単純な部分式ですが、Java の悲惨な型推論が複雑になることを除けば、そうではありません。3 つの異なる状況で推論を行う Scala と比較してください。

于 2013-03-26T05:01:30.243 に答える