[Java] ジェネリックメソッドで可変長引数を使うと警告が出る

2017年10月16日月曜日

Java ジェネリクス

ヒープ汚染

Javaで定数のSetを作ろうとして、以下のようにしたら、コンパイル時に警告が出てしまった。
    private static final Set<String> fruits = readOnlySet("apple", "banana", "orange");

    private static <T> Set<T> readOnlySet(T ... a) {
        Set<T> set = new HashSet<>(a.length);
        Collections.addAll(set, a);
        return Collections.unmodifiableSet(set);
    }
警告: [unchecked] パラメータ化された可変引数型Tからのヒープ汚染の可能性があります
    private static  Set readOnlySet(T ... a) {
                                                ^
  Tが型変数の場合:
    メソッド readOnlySet(T...)で宣言されているTはObjectを拡張します

@SafeVarargs

型変数をメソッドの可変長引数の型にしようとすると、この警告が発生する。
なんで、これに問題があるかというのは以下を見てもらった方がいいけど、警告を抑制するには@SafeVarargsをメソッドのアノテーションとしてつける。

非具象化可能仮パラメータを可変長引数メソッドに使用する場合のコンパイラの警告とエラーの改善
    @SafeVarargs
    private static <T> Set<T> readOnlySet(T ... a) {
        Set<T> set = new HashSet<>(a.length);
        Collections.addAll(set, a);
        return Collections.unmodifiableSet(set);
    }
これで警告がでなくなるはず!っと思ったけど、まだ出る。

警告: [varargs] 可変引数メソッドは、型情報保持可能でない可変引数パラメータaからのヒープ汚染の原因となる可能性があります
        Collections.addAll(set, a);
                                ^

うーん、今度は Collections.addAll を呼び出している箇所で警告が発生している。

-Xlint

で、なんでまだ警告が発生するのかなーと思ってたら、コンパイラのオプションで Xlint を指定しているのが原因だった。

$ javac -Xlint

確かに Xlint のオプション指定を外すと、警告が出なくなる。
でも、諸々の警告を出したいから、指定してるんだから外したくないなー、と思って
非具象化可能仮パラメータを持つ可変長引数メソッドからの警告の抑制
を見ると

コンパイラ・オプション-Xlint:varargsを使用します。

とある。
でも、実はこれをそのまま適用しても警告は消えない。

-Xlintオプションによる警告の有効化または無効化
警告を抑制するには警告名の前にハイフンをつけなければいけない。

$ javac -Xlint:-varargs

これでXlintを使っていても、varargsの警告が出なくなる。