Java : @SafeVarargs (アノテーション) - API使用例
SafeVarargs (Java SE 20 & JDK 20) の使用例まとめです。
@SafeVarargs アノテーションは、メソッドの可変パラメータに対して "安全ではない操作を実行しない" ことを明示します。
API仕様のおともにどうぞ。
概要
@SafeVarargs は、
- メソッドの可変パラメータに対して 安全ではない操作 は実行しない
ということを明示するためのアノテーションです。
メソッドで可変パラメータを使うと、その定義によってはコンパイルで警告が出ることがあります。
その警告の意味をよく理解して、それでもその定義を使いたい … という場合に @SafeVarargs を使うと、警告を抑止することができます。
警告が出る例
警告が出る例を見てみましょう。
ジェネリクス(総称型) の Foo クラスです。
m メソッドは T 型の可変パラメータを受け取ります。
public class Foo<T> {
public void m(T... args) {
System.out.println(Arrays.toString(args));
}
}
さて、この Foo クラスをコンパイルすると、次のような警告が出ます。
> javac -Xlint:unchecked Foo.java
Foo.java:6: 警告: [unchecked] パラメータ化された可変引数型Tからのヒープ汚染の可能性があります
public void m(T... args) {
^
Tが型変数の場合:
クラス Fooで宣言されているT extends Object
警告1個
ちなみに、m メソッドは 安全ではない操作 はしていません。
しかし、コンパイルのたびに警告が出るのは、ちょっといやですよね…
そんなときに使えるのが @SafeVarargs アノテーションです。
public class Foo<T> {
@SafeVarargs
public final void m(T... args) {
System.out.println(Arrays.toString(args));
}
}
m メソッドに @SafeVarargs をつけました。
あと、final もつけています。
これは @SafeVarargs を使う条件の1つです。static でも OK です。
つまり、m メソッドをオーバーライドして 安全ではない操作 を実行する … ということがないようにですね。
それでは、修正した Foo クラスをコンパイルしてみましょう。
> javac -Xlint:unchecked Foo.java
<エラー、警告なし>
無事に、警告なしでコンパイルができました。
ただし、警告が出るからといって、安易に @SafeVarargs を使うことはやめたほうがよいでしょう。
- 本当に警告を抑止してよいのか?
- メソッドのパラメータ定義を変更して改善できないか?
などをよく検討して、どうしても必要であれば @SafeVarargs を使いましょう。
安全ではない操作の例
安全ではない操作のコード例です。
public class Bar {
public static List<String>[] m(List<String>... args) {
if (0 < args.length) {
Object[] objects = args;
objects[args.length - 1] = List.of(123);
}
return args;
}
}
この Bar クラスの m メソッドは、意図的に 安全ではない操作 をしています。
パラメータの args は List<String> の配列ですが、
Object[] objects = args;
objects[args.length - 1] = List.of(123); // List<String> 以外の型を代入!
この操作で、List<Integer> 型のオブジェクトを代入しています。
それでは、Bar.m メソッドを使ってみましょう。
List<String>[] listArray = Bar.m(
List.of("aaa"),
List.of("bbb"),
List.of("ccc")
);
try {
System.out.println("-- listArray --");
for (final var list : listArray) {
if (!list.isEmpty()) {
String str = list.get(0);
System.out.println(str);
}
}
} catch (ClassCastException e) {
System.out.println("ClassCastException!");
}
// 結果
// ↓
//-- listArray --
//aaa
//bbb
//ClassCastException!
使う側は、とくにおかしな操作はしていませんが、ClassCastException が発生してしまいました。
このように、ジェネリクス(総称型)の配列は、操作によっては安全でないことがあります。
そのため、警告が出る例 で見たように、Java のコンパイラが警告で知らせてくれているわけですね。