快速java泛型问题

Lui*_*hys 4 java generics

我不认为我真的了解Java泛型.这两种方法有什么区别?为什么第二个没有编译,错误如下所示.

谢谢

static List<Integer> add2 (List<Integer> lst) throws Exception {
    List<Integer> res = lst.getClass().newInstance();
    for (Integer i : lst) res.add(i + 2);
    return res;
}
Run Code Online (Sandbox Code Playgroud)

.

static <T extends List<Integer>> T add2 (T lst) throws Exception {
    T res = lst.getClass().newInstance();
    for (Integer i : lst) res.add(i + 2);
    return res;
}

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - incompatible types
  required: T
  found:    capture#1 of ? extends java.util.List
Run Code Online (Sandbox Code Playgroud)

Bru*_*eis 6

对于第二种编译方法,您必须将结果转换newInstace()T:

static <T extends List<Integer>> T add2 (T lst) throws Exception {
  T res = (T) lst.getClass().newInstance();
  for (Integer i : lst) res.add(i + 2);
  return res;
}
Run Code Online (Sandbox Code Playgroud)

关于这两种方法的区别,让我们忘记实现,只考虑签名.

在编译代码之后,两个方法将具有完全相同的签名(因此如果具有相同的名称,编译器将给出错误).这是因为所谓的类型擦除.

在Java中,编译后所有类型参数都会消失.它们被最通用的原始类型取代.在这种情况下,两种方法都将编译为List add2(List).

现在,这将显示两种方法之间的区别:

class Main {
  static <T extends List<Integer>> T add1(T lst) { ... }
  static List<Integer> add2(List<Integer> lst) { ... }
  public static void main(String[] args) {
    ArrayList<Integer> l = new ArrayList<Integer>();
    ArrayList<Integer> l1 = add1(l);
    ArrayList<Integer> l2 = add2(l); // ERROR!
  }
}
Run Code Online (Sandbox Code Playgroud)

标记为的// ERROR!行将无法编译.

在第一种方法中,add1编译器知道它可以将结果分配给类型的变量ArrayList<Integer>,因为签名声明方法的返回类型与参数的返回类型完全相同.由于参数是类型ArrayList<Integer>,编译器将推断TArrayList<Integer>,这将允许您将结果分配给ArrayList<Integer>.

在第二种方法中,所有编译器都知道它将返回一个实例List<Integer>.它不能确定它是一个ArrayList<Integer>,所以你必须做一个明确的演员,ArrayList<Integer> l2 = (ArrayList<Integer>) add2(l);.请注意,这不会解决问题:您只是告诉编译器停止抱怨并编译代码.您仍然会收到一个警告(未经检查的强制转换),可以通过使用该方法进行注释来使其静音@SuppressWarnings("unchecked").现在编译器会很安静,但你可能仍然会ClassCastException在运行时获得!