Java Can';t将泛型方法的结果传递给另一个方法?

Java Can';t将泛型方法的结果传递给另一个方法?,java,generics,Java,Generics,我有一个简单的泛型方法,它创建一个包含n个元素的列表并返回它: import java.util.*; class A { public static <T> List<T> init(int n) { List<T> l = new ArrayList<T>(); while (n --> 0) l.add(null); return l; } public stati

我有一个简单的泛型方法,它创建一个包含n个元素的列表并返回它:

import java.util.*;
class A {
    public static <T> List<T> init(int n) {
        List<T> l = new ArrayList<T>();
        while (n --> 0) l.add(null);
        return l;
    }
    public static void main(String[] args) {
        List<String> x = init(5);
        f(x);
    }
    public static void f(List<String> l) {
        System.out.println("l: " + l);
    }
}
但是如果我去掉额外的变量:

import java.util.*;
class A {
    public static <T> List<T> init(int n) {
        List<T> l = new ArrayList<T>();
        while (n --> 0) l.add(null);
        return l;
    }
    public static void main(String[] args) {
        f(init(5));
    }
    public static void f(List<String> l) {
        System.out.println("l: " + l);
    }
}
import java.util.*;
甲级{
公共静态列表初始化(int n){
列表l=新的ArrayList();
而(n-->0)l.add(null);
返回l;
}
公共静态void main(字符串[]args){
f(init(5));
}
公共静态无效f(列表l){
System.out.println(“l:+l”);
}
}
它不再编译

$ javac A.java && java A
A.java:9: f(java.util.List<java.lang.String>) in A cannot be applied to (java.util.List<java.lang.Object>)
        f(init(5));
        ^
1 error
$javac A.java&&javaa
A.java:9:f(java.util.List)不能应用于(java.util.List)
f(init(5));
^
1错误
为什么?

但这是可行的:

import java.util.*;
class A {
    public static <T> List<T> init(int n) {
        List<T> l = new ArrayList<T>();
        while (n --> 0) l.add(null);
        return l;
    }
    public static <T> T id(T t) {
        return t;
    }
    public static void main(String[] args) {
        List<String> x = init(5);
        f(id(x));
    }
    public static void f(List<String> l) {
        System.out.println("l: " + l);
    }
}
import java.util.*;
甲级{
公共静态列表初始化(int n){
列表l=新的ArrayList();
而(n-->0)l.add(null);
返回l;
}
公共静态T id(T T){
返回t;
}
公共静态void main(字符串[]args){
列表x=init(5);
f(id(x));
}
公共静态无效f(列表l){
System.out.println(“l:+l”);
}
}

为什么?

在第一个和第三个例子中,你都说你在谈论一个
列表,但在第二个例子中,你说的是
f(init(5))
,它可能是一个
列表。我不是100%这是唯一的原因,但请检查:)

如果您省略了额外的变量,编译器在调用
init(5)
时无法推断类型参数
T
。它假定
T
Object
,因此编译器错误

将额外变量声明为
列表x
时,编译器将
T
推断为
String

f(init(5));
这将使用直接从
init()
接收的参数调用
f()
。但是,
init()
现在返回
T
列表。对于未指定的
T
,Java只使用
Object
,因为它是所有对象的基类<代码>列表
不是
列表
,因此方法签名和参数不匹配

List<String> x = init(5);
f(x);

这基本上是一样的。现在,由于
x
具有类型
List
id()
的类型
T
String
。这样,
id()
的返回值也是与签名匹配的
List

Java依靠推理来确定在未明确定义类型变量的情况下类型变量是什么

List<String> x = init(5);
f(id(x));
在您的第一个示例中:

List<String> x = init(5);
f(x);
编译器无法推断您正在调用
init
,因为您没有明确地告诉它(通过
A.init(5)
),也没有将它分配给适当的变量


在第三个示例中:

List<String> x = init(5);
f(id(x));
List x=init(5);
f(id(x));
编译器推断您正在调用
id
,它将
列表
返回到
f


编译器在泛型推理方面不太聪明。除非通过使用变量或直接将类型参数传递给方法来明确说明类型参数是什么,否则它将无法找出它们

如果您对细节感到好奇,以下是JLS的相关部分:

    • 首先,修复

      f(A.<String>init(5)); // compiles
      
      f(A.init(5));//汇编
      
      现在,原因是:编译原始代码是因为java可以推断类型,因为它被分配给一个类型化变量。但当传递给类型化参数时,推理不起作用


      该修复程序在调用类型化方法时使用语法显式指定类型。

      如果无法推断类型参数,那么它如何知道绑定到结果的
      列表x
      是有效的?它是隐式铸造吗?我认为Java不会这样做。如果不给编译器一点线索,它就无法推断。当你说x的类型是一个
      List
      时,你给了它一个线索来推断
      init
      的返回类型是一个
      List
      。但是有一个线索,那就是从
      init
      返回的值被传递到
      f
      ,它接受
      List
      。这与
      Math.sqrt(Math.sqrt(123))
      有何不同?请告诉我,我将用一个例子来增强您的答案,值得注意的是,将添加目标类型泛型推理。显然,您的第二个示例将在中起作用。
      List<String> x = init(5);
      f(id(x));
      
      f(A.<String>init(5)); // compiles