ジェネリック型のvarargsを使用すると、Java7でこれが発生することを理解しています。
しかし、私の質問は..
Eclipseが「その使用はヒープを汚染する可能性がある」と言った場合、正確にはどういう意味ですか?
と
@SafeVarargs
新しいアノテーションはこれをどのように防ぎますか?
ジェネリック型のvarargsを使用すると、Java7でこれが発生することを理解しています。
しかし、私の質問は..
Eclipseが「その使用はヒープを汚染する可能性がある」と言った場合、正確にはどういう意味ですか?
と
@SafeVarargs
新しいアノテーションはこれをどのように防ぎますか?
ヒープ汚染は専門用語です。それは、それらが指すオブジェクトのスーパータイプではないタイプを持つ参照を参照します。
List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As
これは「説明できない」ことにつながる可能性がありClassCastException
ます。
// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0);
@SafeVarargs
これをまったく防ぐことはできません。ただし、ヒープを汚染しないことが証明できるメソッドがあり、コンパイラはそれを証明できません。以前は、そのようなAPIの呼び出し元は、完全に無意味であるが、すべての呼び出しサイトで抑制されなければならない迷惑な警告を受け取りました。これで、API作成者は宣言サイトで一度それを抑制することができます。
ただし、この方法が実際に安全でない場合、ユーザーに警告は表示されなくなります。
あなたが宣言するとき
public static <T> void foo(List<T>... bar)
コンパイラはそれをに変換します
public static <T> void foo(List<T>[] bar)
その後に
public static void foo(List[] bar)
その場合、誤って誤った値をリストに割り当て、コンパイラーがエラーをトリガーしないという危険が生じます。たとえば、T
がtheの場合String
、次のコードはエラーなしでコンパイルされますが、実行時に失敗します。
// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;
// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));
// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);
メソッドをレビューして、そのような脆弱性が含まれていないことを確認した場合は@SafeVarargs
、警告を抑制するために注釈を付けることができます。インターフェイスには、を使用します@SuppressWarnings("unchecked")
。
このエラーメッセージが表示された場合:
Varargsメソッドは、再利用不可能なvarargsパラメーターからヒープ汚染を引き起こす可能性があります
そして、あなたはあなたの使用法が安全であると確信しているので、@SuppressWarnings("varargs")
代わりに使用するべきです。@SafeVarargsはこのメソッドの適切なアノテーションですか?を参照してください。この2番目の種類のエラーのわかりやすい説明については、https ://stackoverflow.com/a/14252221/14731を参照してください。
参照:
@SafeVarargs
それが起こるのを防ぐことはできませんが、それを使用するコードをコンパイルするときはコンパイラーがより厳密であることを義務付けています。
http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.htmlで、これについてさらに詳しく説明しています。
ヒープ汚染とはClassCastException
、ジェネリックインターフェイスで操作を実行するときに取得し、宣言されたものとは別のタイプが含まれている場合です。
varargsを使用するとObject[]
、引数を保持するためのが作成される可能性があります。
エスケープ分析により、JITはこのアレイの作成を最適化できます。(私が見つけた数少ない機会の1つです)最適化される保証はありませんが、メモリプロファイラーに問題がない限り心配する必要はありません。
AFAIK@SafeVarargs
は、コンパイラによる警告を抑制し、JITの動作を変更しません。
その理由は、varargsには、パラメーター化されていないオブジェクト配列で呼び出されるオプションがあるためです。したがって、タイプがList <A> ...の場合、List[]非varargsタイプで呼び出すこともできます。
次に例を示します。
public static void testCode(){
List[] b = new List[1];
test(b);
}
@SafeVarargs
public static void test(List<A>... a){
}
ご覧のとおり、List [] bには任意のタイプのコンシューマーを含めることができますが、このコードはコンパイルされます。varargsを使用する場合は問題ありませんが、型消去後にメソッド定義を使用する場合(void test(List []))、コンパイラーはテンプレートパラメーターの型をチェックしません。@SafeVarargsはこの警告を抑制します。
@SafeVarargs
メソッドの呼び出し方法(クラスのプライベートメソッドなど)を制御できる場合は、メソッドにアノテーションを追加する方が安全です。宣言されたジェネリック型のインスタンスのみがメソッドに渡されることを確認する必要があります。
メソッドがライブラリとして外部に公開されると、そのような間違いを見つけるのが難しくなります。この場合、このアノテーションを避けCollection<Type1<Type2>>
、varargs()の代わりにコレクションタイプ(例)inputを使用してソリューションを書き直すのが最善Type1<Type2>...
です。
ネーミングに関しては、ヒープ汚染現象という用語は私の意見ではかなり誤解を招きます。ドキュメントでは、実際のJVMヒープはイベントに記載されていません。この現象の命名に関するいくつかの興味深い考えを含むソフトウェアエンジニアリングの質問があります。