Java 7のひし形演算子(<>)のポイントは何ですか?


445

Java 7のdiamond演算子では、次のようなコードを使用できます。

List<String> list = new LinkedList<>();

ただし、Java 5/6では、次のように簡単に記述できます。

List<String> list = new LinkedList();

型消去についての私の理解は、これらはまったく同じであるということです。(とにかくジェネリックは実行時に削除されます)。

なぜダイヤモンドを気にするのですか?どのような新しい機能/タイプセーフが許可されますか?新しい機能が得られない場合、なぜそれを機能と呼ぶのですか?この概念に対する私の理解に欠陥はありますか?


4
Javaはメソッド呼び出しで型推論を行うため、静的ファクトリメソッドを使用する場合、これは問題ではないことに注意してください。
ブライアンゴードン

警告を無効にすると、実際には役に立たない... :)私のように
Renetik

3
それは答えたが、それは(ページの中央付近)のJavaチュートリアルにもありますされています:docs.oracle.com/javase/tutorial/java/generics/...
アンドレアスTasoulasを

これに関するdZoneの素晴らしい記事。
R.ウースターホルト2015年

2
私の見解では、これはList <String>の構文シュガーであると考えています。list = new LinkedList <左側のタイプが何であれ>(); つまり、汎用性を保つ
Viktor Mellgren

回答:


496

の問題

List<String> list = new LinkedList();

左側ではジェネリック型を使用してList<String>おり、右側ではロー型を使用していますLinkedList。Javaのraw型は、実質的にはジェネリックス前のコードとの互換性のためにのみ存在し、絶対に必要でない限り、新しいコードで使用すべきではありません。

さて、Javaに最初からジェネリックがあり、ジェネリックを持つLinkedList前に最初に作成されたような型がなかった場合、ジェネリック型のコンストラクターが左からその型パラメーターを自動的に推論するようにした可能性があります-可能であれば、割り当ての手前。しかし、そうではなく、下位互換性のためにraw型とジェネリック型を異なる方法で処理する必要があります。そのため、型パラメーターを繰り返す必要なしに、汎用オブジェクトの新しいインスタンスを宣言するわずかに異なるが、同様に便利な方法であるダイヤモンド演算子を作成する必要があります。

の元の例に関する限りList<String> list = new LinkedList()、コンパイラーは、割り当てが必要なため、その割り当てに対して警告を生成します。このことを考慮:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

ジェネリックは、間違った処理を行わないようにコンパイル時に保護するために存在します。上記の例では、rawタイプを使用すると、この保護が適用されず、実行時にエラーが発生します。これが、生の型を使用してはならない理由です。

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

ただし、ひし形演算子を使用すると、割り当ての右側を左側と同じ型パラメーターを持つ真の総称インスタンスとして定義できます。これらのパラメーターを再度入力する必要はありません。これにより、rawタイプを使用する場合とほぼ同じ労力でジェネリックの安全性を維持できます。

理解すべき重要なことは、生の型(なし<>)はジェネリック型と同じように扱うことができないということです。生の型を宣言すると、ジェネリックの利点と型チェックがまったく得られません。また、ジェネリックスはJava言語の汎用部分であることを覚えておく必要があります。これらは、Collectionsの引数のないコンストラクターに適用されるだけではありません。


31
下位互換性は優れていますが、複雑さを犠牲にしないでください。Java 7が-compatibilityコンパイラスイッチを導入できないのはなぜですか?これがない場合、javacすべてのraw型が禁止され、厳密にジェネリック型のみが適用されますか?これにより、コードがより簡潔になります。
Rosdi Kasim

3
@Rosdi:そうですね、新しいコードでは生の型は必要ありません。ただし、ソースファイルにJavaのバージョン番号を含めることを強くお勧めします(コマンドラインを(誤って)使用するのではなく)。私の回答を参照してください。
maaartinus、2011

37
私は個人的に、あなたが同じラインで定義しインスタンス化しているのでなければ、ダイヤモンドの使用を好みません。List<String> strings = new List<>()でも問題ありませんが、を定義してprivate List<String> my list;から、インスタンス化するページの半分まで下がるとmy_list = new List<>()、クールではありません。リストには何が含まれていますか?ああ、定義を探しましょう。突然、ダイヤのショートカットの利点は、さようならなくなります。
rmir​​abelle 2013

11
@rmirabelleそれはどう違うのmy_list = getTheList()ですか?この種の問題に対処するには、いくつかのより良い方法があります。1.マウスをホバーすると変数のタイプを表示するIDEを使用します。2.など、より意味のある変数名を使用します。3 private List<String> strings.本当に必要でない限り、変数の宣言と初期化を分割しないでください。
Natix 2014年

1
@Morfidon:はい、Java 8でも有効です。警告が表示されるはずです。
ColinD

36

あなたの理解には少し欠陥があります。ダイアモンド演算子は、自分で繰り返す必要がないため、優れた機能です。型を宣言するときに一度型を定義することは理にかなっていますが、右側で再度定義することは意味がありません。DRYの原則。

次に、型の定義に関するすべてのファズを説明します。型は実行時に削除されますが、型定義を使用してリストから何かを取得したい場合は、リストを宣言するときに定義した型としてそれを取得します。そうしないと、特定の機能がすべて失われ、取得したオブジェクトを元の型にキャストする場合を除いて、オブジェクトの機能。これは、非常に扱いにくく、ClassCastExceptionが発生する場合があります。

を使用List<String> list = new LinkedList()すると、rawtype警告が表示されます。


8
List<String> list = new LinkedList()正しいコードです。あなたはこれを知っており、私もこれを知っています。そして、(私が理解しているように)問題は、なぜこのコードが非常に安全であることをJavaコンパイラだけが理解できないのかということです。
ローマン

22
@Roman:List<String> list = new LinkedList()は正しいコードではありません。確かに、それがあったらいいですね!また、Javaに最初からジェネリックがあり、ジェネリックではないジェネリック型の下位互換性を処理する必要がなかった場合は、おそらくそうでしたが、実際はそうでした。
ColinD

6
@ColinD Javaは、各単一行の下位互換性を処理する必要はありません。総称を使用するJavaソースファイルでは、古い非総称型を禁止し(<?>レガシーコードに接続する場合は常に使用できます)、役に立たないひし形演算子が存在しないようにする必要があります。
maaartinus、2011年

16

この行により、[チェックされていない]警告が発生します。

List<String> list = new LinkedList();

それで、質問が変形します:新しいコレクションが作成された場合にのみ、なぜ[未チェック]警告が自動的に抑制されないのですか?

<>機能を追加するよりもはるかに難しい作業だと思います。

UPD:「いくつかの目的のためだけに」raw型を合法的に使用するのであれば、混乱があると私は思います。


13

理論的には、ひし形演算子を使用すると、繰り返し型引数を保存することで、よりコンパクトな(そして読みやすい)コードを記述できます。実際には、混乱を招く文字が2つあるだけで、何も得られません。どうして?

  1. 新しいコードで生の型を使用する正気なプログラマはいない。そのため、コンパイラーは、型引数を記述しないことで、それらに推測させたいと単純に想定することができます。
  2. diamond演算子は型情報を提供せず、コンパイラーに「問題ない」と表示します。したがって、省略しても害はありません。diamond演算子が合法である場所では、コンパイラーによって「推測」される可能性があります。

私見、ソースをJava 7としてマークするための明確でシンプルな方法があれば、このような奇妙なことを発明するよりも便利でしょう。このようにマークされたコードでは、生の型は何も失うことなく禁止できます。

ところで、私はそれがコンパイルスイッチを使用して行われるべきではないと思います。プログラムファイルのJavaバージョンはファイルの属性であり、オプションはまったくありません。取るに足らないものを使う

package 7 com.example;

明確にすることができます(1つ以上の豪華なキーワードを含む、より洗練されたものを好むかもしれません)。異なるJavaバージョン用に作成されたソースを問題なく一緒にコンパイルすることもできます。これにより、互換性を失うことなく、新しいキーワード(「モジュール」など)を導入したり、古い機能(1つのファイルに複数の非パブリック非ネストクラスなど)を削除したりできます。


2
あなたは違いを検討しているnew ArrayList(anotherList)new ArrayList<>(anotherList)(それが割り当てられている場合は特にList<String>してanotherListいるがList<Integer>)?
Paul Bellora

@Paul Bellora:いいえ。驚くべきことに、どちらもコンパイルされています。ダイヤのあるものは警告も出されていません。しかし、これには意味がありません。詳しく説明できますか?
maaartinus 2013年

申し訳ありませんが、私はあまりよく説明しませんでした。これら2つの例の違いを確認してください:ideone.com/uyHaghideone.com/ANkg3T少なくともジェネリックバウンドの引数が渡されている場合は、生の型の代わりにダイヤモンド演算子を使用することが重要であることを指摘しています。インチ
ポールBellora

実際、私はColinDの回答を読むのに時間をかけていませんでした-彼はほとんど同じことを引用しています。
Paul Bellora 2013年

2
したがって、生の型の新しい構文を導入しようとしている場合は、それが本当に必要ないくつかの場所で、のようなものを使用してみませんかnew @RawType List()。これはすでに有効なJava 8構文と型注釈で、必要な場所すべてで使用できます@RawType List = (@RawType List) genericMethod();。適切なもの@SuppressWarningsが配置されていない限り、生の型が現在コンパイラ警告を作成することを考慮@RawTypeすると、合理的な置き換えとなり、より微妙な構文は必要ありません。
Holger

8

を記述するとList<String> list = new LinkedList();、コンパイラは「チェックされていない」警告を生成します。無視してもかまいませんが、これらの警告を無視していた場合は、実際のタイプセーフティの問題について通知する警告を見逃す可能性もあります。

そのため、余分な警告を生成しないコードを記述することをお勧めします。また、ダイヤモンドオペレーターを使用すると、不必要な繰り返しをせずに便利な方法でそれを行うことができます。


4

他の応答で述べたすべては有効ですが、ユースケースは完全に有効なIMHOではありません。Guava 、特にコレクションに関連するものをチェックアウトする場合、静的メソッドを使用して同じことが行われています。たとえばLists.newArrayList()を使用すると、

List<String> names = Lists.newArrayList();

または静的インポート

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

Guavaにはこのような他の非常に強力な機能があり、実際には<>の多くの用途を考えることはできません。

もしひし形演算子の振る舞いをデフォルトにする、つまり式の左側から型が推論された場合、または左側の型が右側から推論された場合、それはより有用でした。後者はScalaで何が起こるかです。


3

diamond演算子のポイントは、ジェネリック型を宣言するときにコードの入力を減らすことです。ランタイムには何の影響もありません。

Java 5と6で指定した場合の唯一の違い

List<String> list = new ArrayList();

あなたが指定しなければならないということである@SuppressWarnings("unchecked")list(そうしないと、未チェックのキャスト警告が表示されます)。私の理解では、ダイヤモンドオペレーターは開発を容易にすることを試みています。ジェネリックのランタイム実行には何の関係もありません。


その注釈を使用する必要さえありません。少なくともEclipseでは、これについて警告しないようにコンパイラーに指示するだけで問題ありません
Xerus

アノテーションを付けることをお勧めします。すべての開発者がここでEclipseを使用しているわけではありません。
ブハケシンディ2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.