10

私の理解によると、Javaの次の汎用関数:

public static <T> T f(T x) {
   Integer[] arr = new Integer[4];
   T ret = (T) arr[2];
   return ret;
}

次の形式にコンパイルされます (無制限であるため)。

public static Object f(Object x) {
   Integer[] arr = new Integer[4];
   Object ret = (Object) arr[2];
   return ret;
}

ただし、次のステートメントを実行すると、コンパイラは戻り値が整数型であると判断できます。コンパイラはどのようにそれを理解しますか?

Integer i = f(new Integer(4));

上記のステートメントが機能するためには、関数を次のように記述すべきではありませんか?

  public static <T extends Integer> T f(T x) {
       Integer[] arr = new Integer[4];
       T ret = (T) arr[2];
       return ret;
    }
4

3 に答える 3

7

ジェネリックは型消去を使用します。これは基本的に、ジェネリックが暗黙のキャストにすぎないことを意味するため、次のようにします。

List<Integer> ...

通常と変わらず、実際には s などListを含む場合があります。IntegerJava に(およびその他のもの)にキャストget()するように指示しているだけです。Integer型は実行時に保持されません (ほとんどの場合)。

配列は異なります。配列は共変と呼ばれるものです。つまり、それらの型は実行時に保持されます。したがって、次のことができます。

List<Integer> list1 = new ArrayList<Integer>();
list2 = (List<String>)list1;
list2.add("hello");

これは完全に合法であり、コンパイルして実行します。しかし:

Integer[] arr1 = new Integer[10];
String[] arr2 = (String[])arr1; // compiler error

しかし、それはそれよりも微妙になります。

Integer[] arr1 = new Integer[10];
Object[] arr2 = (Object[])arr1;
arr2[5] = "hello"; // runtime error!

あなたの機能について。あなたが書くとき:

public static <T> T f(T x) {
  Integer[] arr = new Integer[4];
  T ret = (T) arr[2];
  return ret;
}

Tコンパイラに、引数の型と戻り値の型である を引数から派生させるように指示しています。したがって、 を渡すとInteger、戻り値の型は になりIntegerます。電話すると:

Integer i = f(new Integer(4));

コンパイラはあなたの指示に従っているだけです。関数はObjectコンパイルされた形式で取得して返しますが、これを行うだけです:

Integer i = (Integer)f(new Integer(4));

暗黙的に。

上記の例のように、パラメーター化された型に基づいて返されるはずのものではなく、好きなものを返すことをList止めるものは何もありません。f()

于 2010-02-03T03:08:36.413 に答える
6

このトピックについてより詳しい知識を持っている人が後で参加するかもしれませんが、私の謙虚な説明は次のとおりです。

あなたの言うことは正しいです、ジェネリックには型消去があります。つまり、ジェネリック情報は実行時に利用できません。ただし、コンパイル時に利用可能であり、それがコンパイラが型が混在していることを理解できる方法です。

それが役立つことを願っています!

于 2010-02-03T03:04:07.157 に答える
2

あなたの例では、コンパイラは、戻り値の型が関数に渡されたパラメーターと同じ型であると判断します。これは、両方が T 型にパラメーター化されているためです。これは に解決されますInteger。バイトコードを生成すると、型パラメータ情報が消去されます。

于 2010-02-03T03:04:36.093 に答える