1. ホーム
  2. java

[解決済み] Java SafeVargsアノテーション、標準やベストプラクティスは存在するのか?

2022-04-19 06:35:21

質問

私は最近、Javaの @SafeVarargs というアノテーションがあります。Javaで可変長関数を安全でなくするものについてググると、かなり混乱しました(ヒープポイズニング? 消去された型?

  1. で可変長のJava関数が安全でない理由は何ですか? @SafeVarargs の意味で、(できれば詳細な例の形で説明されるのが望ましい)?

  2. なぜこのアノテーションはプログラマーの裁量に任されているのでしょうか?これはコンパイラがチェックできるはずのものではないのですか?

  3. 自分の関数が本当にバラグセーフであることを確認するために、守らなければならない基準がありますか?もしそうでなければ、それを保証するためのベストプラクティスは何でしょうか?

どのように解決するのですか?

1) ジェネリックとバラグに関する特殊な問題については、インターネットやStackOverflowに多くの事例があります。基本的には、型-パラメータ型の引数を可変個にした場合です。

<T> void foo(T... args);

Java では、varargs は構文上の糖であり、コンパイル時に単純に "書き直し" が行われます。 X... のパラメータに変換されます。 X[] そして、この varargs メソッドを呼び出すたびに、コンパイラは varargs パラメータに入るすべての "variable arguments" を収集し、次のような配列を作成します。 new X[] { ...(arguments go here)... } .

これは、varargs の型が次のような具象的なものである場合にうまく機能します。 String... . のような型変数である場合 T... の場合にも有効です。 T は、その呼び出しの具象型であることが知られています。例えば、上記のメソッドがクラス Foo<T> があり、その中に Foo<String> を参照すると foo がわかっているので、問題ないでしょう。 TString を、その時点のコードで表示します。

しかし、quot;value" の場合はうまくいきません。 T は別の型パラメータです。Javaでは、型パラメータを構成する型の配列( new T[] { ... } ). そこでJavaでは、代わりに new Object[] { ... } (ここで Object の上界です。 T もし、その上限が何か違うものであったなら、それは Object ) と、コンパイラの警告が表示されます。

では new Object[] の代わりに new T[] などと言うのでしょうか?さて、Javaの配列は、実行時にその構成要素の型を知ることができます。したがって、渡された配列オブジェクトは、実行時に間違った成分型を持つことになります。

おそらく最も一般的な varargs の使い方である、単に要素を反復処理する場合、これは問題ありません(配列の実行時の型は気にしない)ので、これは安全です。

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

しかし、渡された配列の実行時構成要素型に依存するものについては、安全とは言えません。以下は、安全でなくクラッシュするものの簡単な例です。

class UnSafeVarargs
{
  static <T> T[] asArray(T... args) {
    return args;
  }

  static <T> T[] arrayOfTwo(T a, T b) {
    return asArray(a, b);
  }

  public static void main(String[] args) {
    String[] bar = arrayOfTwo("hi", "mom");
  }
}

ここで問題なのは、「Subject」の型に依存していることです。 args になるように T[] として返すために T[] . しかし、実際には実行時の引数の型は T[] .

3) メソッドに型の引数がある場合 T... (ここでTは任意の型パラメータ)である。

  • 安全です。のインスタンスであることにのみ依存する場合、そのメソッドは安全です。 T
  • 安全でない 配列のインスタンスが T[]

配列の実行時型に依存するものには、以下のようなものがあります:型として返すこと T[] のパラメータに引数として渡す。 T[] を使用して配列の型を取得します。 .getClass() のように、配列の実行時の型に依存するメソッドに渡します。 List.toArray()Arrays.copyOf() など。

2) 上で述べたような区別は複雑すぎて、簡単に自動で区別することはできない。