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> ...

它与普通的没有什么不同,List并且可能包含Integers 或任何东西。您只是在告诉 Java 转换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 回答