Java 101:基本的なJava言語機能ツアー、パート5

前へ12ページ2 2/2ページ

ジェネリッククラスと非ジェネリッククラスの型推論とジェネリックコンストラクター

ジェネリッククラスと非ジェネリッククラスは、コンストラクターが正式な型パラメーターリストを持つジェネリックコンストラクターを宣言できます。たとえば、ジェネリックコンストラクターを使用して次のジェネリッククラスを宣言できます。

 public class Box { public  Box(T t) { // ... } } 

この宣言はBox、仮型パラメーターを持つジェネリッククラスを指定しますE。また、仮型パラメーターを持つジェネリックコンストラクターも指定しますT。次のように、ジェネリッククラスをインスタンス化し、そのコンストラクターを呼び出すことができます。

 new Box("Aggies") 

この式は、のインスタンスを作成Box渡し、MarbleE。また、コンストラクターの引数はオブジェクトであるため、コンパイラーはの実際の型引数Stringとして推測します。TString

Java 7より前のコンパイラは、ジェネリックメソッドの引数と同様にジェネリックコンストラクタの実際の型引数を推測します。ただし、Java 7のコンパイラは、ダイヤモンド演算子のコンテキストでインスタンス化されている汎用クラスの実際の型引数を推測できます。次の例を考えてみましょう。

 Box box = new Box("Aggies"); 

コンパイラーは、ジェネリッククラスのMarble仮型パラメーターの型を推測するだけでなく、このジェネリッククラスのコンストラクターの仮型パラメーターの型も推測します。EBoxStringT

プロジェクトコインの小さな変更#8:簡略化された可変引数メソッドの呼び出し

Javaの7の前に、可変引数(としても知られている変数、引数、起動する各試行変数アリティ非reifiableと)メソッドは、型を出力する「危険運転」警告をコンパイラを引き起こした可変引数。多くの同様の警告メッセージ(呼び出しサイトごとに1つ)の可能性を排除するために、Java7は警告を呼び出しサイトからメソッド宣言に移動しました。

再利用可能および再利用不可能なタイプ

reifiableタイプは、実行時にその完全な型情報を公開します。例には、プリミティブ型、非ジェネリック型、raw型、およびバインドされていないワイルドカードの呼び出しが含まれます。対照的に、再定義不可能な型では、ジェネリックスの前に作成されたJavaライブラリおよびアプリケーションとのバイナリ互換性を確保するために、コンパイル時に型消去によって型情報が削除されます。例にはとが含まSetSetます。非reifiableタイプは、実行時に完全に利用できないため、JVMは違い言うことができないSetとしますSet。実行時には、raw型のみSetが使用可能です。

vararg入力パラメーターを含むジェネリックメソッドは、ヒープ汚染を引き起こす可能性があります。この場合、パラメーター化された型の変数は、そのパラメーター化された型ではないオブジェクトを参照します(たとえば、生の型がパラメーター化された型と混合されている場合)。パラメータ化された型(キャストやメソッド呼び出しなど)を含む操作の正確性を検証できないため、コンパイラは「未チェックの警告」を報告します。

リスト13は、非可変引数のコンテキストでのヒープ汚染を示しています。

リスト13.非可変引数のコンテキストでのヒープ汚染のデモンストレーション

 import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class HeapPollutionDemo { public static void main(String[] args) { Set s = new TreeSet(); Set ss = s; // unchecked warning s.add(new Integer(42)); // another unchecked warning Iterator iter = ss.iterator(); while (iter.hasNext()) { String str = iter.next(); // ClassCastException thrown System.out.println(str); } } } 

変数のss型はパラメーター化されていますSet。ときjava.util.Setによって参照されるということsに割り当てられているss、コンパイラは未チェック警告を生成します。これは、コンパイラが型をs参照していることを判別できないためですSet(そうではありません)。その結果、ヒープが汚染されます。 (コンパイラーでは、この割り当てにより、ジェネリックスをサポートしないレガシーJavaバージョンとの下位互換性を維持できます。さらに、型消去はに変換SetされSet、その結果、1つSetが別のバージョンに割り当てられSetます。)

コンパイラは、Setadd()メソッドを呼び出す行に2番目のチェックされていない警告を生成します。これは、変数sがaSetまたはSettypeを参照しているかどうかを判別できないためです。これは別のヒープ汚染状況です。(コンパイラは、このメソッド呼び出しを可能にするための消去変換Setboolean add(E e)に方法boolean add(Object o)を含む、セットへのオブジェクトの任意の種類を追加することができ、java.lang.Integerのサブタイプjava.lang.Object)。

ヒープ汚染は、可変引数のコンテキストで簡単に発生する可能性があります。たとえば、リスト14について考えてみます。

リスト14.可変引数のコンテキストでのヒープ汚染のデモンストレーション

 import java.util.Arrays; import java.util.List; public class UnsafeVarargsDemo { public static void main(String[] args) { unsafe(Arrays.asList("A", "B", "C"), Arrays.asList("D", "E", "F")); } static void unsafe(List... l) { Object[] oArray = l; oArray[0] = Arrays.asList(new Double(3.5)); String s = l[0].get(0); } } 

Object[] oArray = l;割り当ては、ヒープ汚染の可能性を紹介します。varargsパラメーターのパラメーター化されたタイプと一致しない値lを変数に割り当てることができますoArray。ただし、コンパイラは、に変換List... lするときにすでにチェックされていないため、チェックされていない警告を生成しませんList[] l。変数lのタイプはList[]、サブタイプであるため、この割り当ては有効ですObject[]

また、コンパイラはList、任意のタイプのオブジェクトをoArrayの配列コンポーネントに割り当てるときに警告やエラーを発行しません。たとえば、oArray[0] = Arrays.asList(new Double(3.5));。この割り当ては、最初の配列要素に割り当てる単一含むオブジェクトオブジェクト。oArrayListjava.lang.Double

String s = l[0].get(0);割り当ては問題があります。変数の最初の配列要素に格納されたオブジェクトは、lタイプを有しているListが、この割り当ては型のオブジェクトを期待しますList。その結果、JVMはをスローしjava.lang.ClassCastExceptionます。

このソースコードをコンパイルします(javac -Xlint:unchecked UnsafeVarargsDemo.java)。Java SE 7 update 6でコンパイルすると、次の出力(読みやすくするために少し再フォーマットされています)を確認する必要があります。

 UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array creation for varargs parameter of type List[] unsafe(Arrays.asList("A", "B", "C"), ^ UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from parameterized vararg type List static void unsafe(List... l) ^ 2 warnings 

Java 101のジェネリックスの紹介で、配列作成式で型パラメーターを使用することはできないと述べました。たとえば、を指定することはできませんelements = new E[size];。これを行おうとすると、コンパイラは「ジェネリックアレイ作成エラー」メッセージを報告します。ただし、ジェネリック配列を作成することは可能ですが、それはvarargsコンテキストでのみであり、それが最初の警告メッセージが報告しているものです。舞台裏では、コンパイラはに変換してList... lからに変換List[] lList[] lます。

ヒープ汚染警告がunsafe()メソッドの宣言サイトで生成されることに注意してください。このメッセージは、Java 5および6コンパイラの場合のように、このメソッドの呼び出しサイトでは生成されません。

すべての可変引数メソッドがヒープ汚染に寄与するわけではありません。ただし、メソッドの宣言サイトでは引き続き警告メッセージが発行されます。メソッドがヒープの汚染に寄与しないことがわかっている場合は、@SafeVarargsアノテーションを使用して宣言することで、この警告を抑制することができます。Java7ではjava.lang.SafeVarargsアノテーションタイプが導入されました。たとえば、ArraysクラスのasList()メソッドがヒープ汚染に寄与する方法がないため、このメソッドの宣言には@SafeVarargs次のように注釈が付けられています。

 @SafeVarargs public static  List asList(T... a) 

@SafeVarargs注釈は、一般的な配列の作成とヒープ汚染警告メッセージを排除します。これはメソッドのコントラクトの文書化された部分であり、メソッドの実装がvarargs仮パラメーターを不適切に処理しないことを主張します。

結論として

Java 7は、新しいAutoCloseableインターフェイス、スイッチオン文字列、マルチキャッチ、最終再スロー、バイナリリテラル、数値リテラルの下線、コンパイラのタイプの変更とともに、try-with-resourcesステートメントを介した自動リソース管理を導入することにより、開発者の生産性を向上させました。いわゆるdiamond演算子を導入した推論アルゴリズム、および簡略化されたvarargsメソッドの呼び出し。で次はJavaの101:次世代シリーズは、Java 8のラムダと機能インターフェース言語の特徴を見ています。

このストーリー「Java101:基本的なJava言語機能ツアー、パート5」は、もともとJavaWorldによって公開されました。