JDK 7:ダイヤモンドオペレーター

Project Coinは、新しいJDK7機能のサブセットとして多数の「小さな言語拡張」を提供します。最近、Project CoinのStringsの切り替えについてブログを書きました。この投稿では、新しいDiamond Operator()について書いています。

ダイヤモンド演算子は、コンパイラーにジェネリッククラスのコンストラクターのパラメーター型を推測させることにより、ジェネリックを取り巻くJavaの冗長性の一部を減らします。ダイヤモンド演算子をJava言語に追加するための最初の提案は、2009年2月に行われ、次の簡単な例が含まれています。

たとえば、次の割り当てステートメントについて考えてみます。

地図 アナグラム=新しいHashMap ();

これはかなり長いので、次のように置き換えることができます。

地図 anagrams = new HashMap();

Jeremy Mansonの提案(Project Coinのアイデアの要求に応えた最初の1つ)で提供された上記の例は単純ですが、JDK7でDiamondOperatorがどのように適用されるかを適切に示しています。Mansonの提案は、この追加の理由にも重要な意味を与えます。望ましいものでした:

タイプパラメータが不必要に複製されるという要件

これは不幸を助長します

型推論のために、静的ファクトリメソッドが多すぎる

メソッドの呼び出しで機能します。

言い換えると、JDK 7 ProjectCoinにDiamondOperatorを追加すると、メソッドで使用できるコンストラクターに型推論がもたらされます。メソッドでは、明示的なパラメーター型の指定を省略すると、型推論が暗黙的に行われます。一方、インスタンス化では、型を推測するようにコンパイラーに「指示」するために、diamond演算子を明示的に指定する必要があります。

Mansonは最初の提案で、特別なダイヤモンド演算子のない構文を使用して、インスタンス化の型を暗黙的に推測することはできないと指摘しています。「下位互換性のために、新しいMap()は生の型を示しているため、型に使用できないためです。推論。" JavaチュートリアルのJava言語を学ぶためのジェネリックスレッスンの型推論ページには、Java SE 7を反映するようにすでに更新されている「ジェネリッククラスの型推論とインスタンス化」というセクションが含まれています。このセクションでは、特別な理由についても説明します。インスタンス化時に型推論を使用するようにコンパイラーに明示的に通知するには、演算子を指定する必要があります。

ジェネリッククラスのインスタンス化中に自動型推論を利用するには、diamond演算子を指定する必要があることに注意してください。次の例では、HashMap()コンストラクターがMapではなくHashMap raw型を参照しているため、コンパイラーは未チェックの変換警告を生成します。 タイプ

効果的なJavaの第2版の項目24(「チェックされていない警告を排除する」)で、JoshBlochは太字のテキストで「可能な限りすべてのチェックされていない警告を排除する」と強調しています。Blochは、宣言の右側でraw型を使用するコードをコンパイルするときに発生する未チェックの変換警告の例を示しています。次のコードリストは、この警告につながるコードを示しています。

final Map
     
       statesToCities = new HashMap(); // raw! 
     

次の2つの画面スナップショットは、上記のコード行に対するコンパイラの応答を示しています。最初の画像は、-Xlint警告が有効になっていない場合のメッセージを示し、2番目の画像は、-Xlint:uncheckedがjavacへの引数として提供された場合に発生するより明示的な警告を示しています。

効果的なJavaの場合、Blochは、この特定のチェックされていない警告は、汎用クラスのインスタンス化にパラメータータイプを明示的に提供することで簡単に対処できると指摘しています。JDK 7を使用すると、これはさらに簡単になります。多くの場合、これらの型名で明示的なテキストを追加する必要はなく、型を推測できます。diamond演算子の指定により、生の型を使用するのではなく、この推測を行うようコンパイラーに指示されます。

次のJavaコードリストは、これらの概念の単純な例を示しています。生のセットのインスタンス化、パラメータータイプの明示的な指定によるセットのインスタンス化、およびダイヤモンド演算子()の指定のために推測されたパラメータータイプによるセットのインスタンス化を示すメソッドがあります。

package dustin.examples; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static java.lang.System.out; /** * Very simple demonstration of JDK 7's/Project Coin's "Diamond Operator." */ public class DiamondOperatorDemo { /** Use of "raw" type. */ private static Set rawWithoutExplicitTyping() { final Set names = new HashSet(); addNames(names); return names; } /** Explicitly specifying generic class's instantiation parameter type. */ private static Set explicitTypingExplicitlySpecified() { final Set names = new HashSet(); addNames(names); return names; } /** * Inferring generic class's instantiation parameter type with JDK 7's * 'Diamond Operator.' */ private static Set explicitTypingInferredWithDiamond() { final Set names = new HashSet(); addNames(names); return names; } private static void addNames(final Set namesToAddTo) { namesToAddTo.add("Dustin"); namesToAddTo.add("Rett"); namesToAddTo.add("Homer"); } /** * Main executable function. */ public static void main(final String[] arguments) { out.println(rawWithoutExplicitTyping()); out.println(explicitTypingExplicitlySpecified()); out.println(explicitTypingInferredWithDiamond()); } } 

上記のコードをコンパイルすると、「生の」ケースのみが警告になります。

この時点で、javapがこれらの3つの方法について何を教えてくれるかを見るのは洞察に満ちています。この場合、これは次のコマンドで実行され-vます(verboseのオプションは、すべてのジューシー-pな詳細を提供し、privateメソッドのこれらのジューシーな詳細を表示します)。

javap -v -p -classpath classes dustin.examples.DiamondOperatorDemo 

これらのメソッドはすべて単一のクラスに含まれていたため、クラス全体に対して単一の出力ストリームがあります。ただし、比較しやすくするために、出力をカットアンドペーストして、各メソッドのjavap出力を相互に整列させる形式にしました。各列は、javapいずれかのメソッドの出力を表します。特定のメソッドのフォントの色を青に変更して目立たせ、その列の出力にラベルを付けました。

メソッド自体の名前を除いて、javap出力に違いはありません。これは、Javaジェネリックの型消去は、実行時に型に基づく区別が利用できないことを意味するためです。GenericsのJavaチュートリアルには、これを説明する型消去と呼ばれるページが含まれています。

コンパイラは、コンパイル時に実際の型引数に関するすべての情報を削除します。

新しいコードがレガシーコードとのインターフェースを継続できるように、型消去が存在します。その他の理由でraw型を使用することは、プログラミングの不適切な方法と見なされるため、可能な限り避ける必要があります。

上記の引用が思い出させるように、消去とは、raw型をバイトコード化することは、明示的に型指定されたパラメーター型と同じであるだけでなく、レガシーコードとの統合を除いてraw型を使用しないことを開発者に奨励することを意味します。

結論

Java SE 7にdiamond演算子()が含まれているということは、ジェネリッククラスをインスタンス化するコードの冗長性を減らすことができることを意味します。一般的なコーディング言語、特にJavaは、設定より規約、例外による構成、明示的な指定を必要とせずにできるだけ頻繁に推測するなどのアイデアに向かっています。動的に型付けされた言語は型推論でよく知られていますが、静的に型付けされたJavaでさえ、これよりも多くのことを実行できます。ダイヤモンド演算子はその一例です。

//marxsoftware.blogspot.com/で利用可能な元の投稿

このストーリー「JDK7:The Diamond Operator」は、もともとJavaWorldによって公開されました。