我试图了解Lists.newArrayList()
如何知道要返回的列表类型。
我看到了函数newArrayList()
的{{3}},但它只返回了通用类型E的ArrayList
。
public static <E> ArrayList<E> newArrayList() {
return new ArrayList<E>();
}
但是,当我调用该函数时,我没有传递任何此类信息。
List<String> testList = Lists.newArrayList();
它如何知道我想要的ArrayList
类型?
我读过有关泛型和TypeToken
的内容,但无法通过代码与之相关。
答案 0 :(得分:14)
因为编译器可以从变量声明中推断出类型。
示例:
List<String> list = Lists.newArrayList(),
编译器将理解集合的类型(E的类型)将为<String>
,因为您希望获得字符串列表。
避免重写整个&lt;&gt;是有用的。参数,但使用Java7和菱形运算符,您可以使用
来避免它List<String> list = new ArrayList<>();
(想象一下,List<List<String>>
你应该重写List<List<String>>
)
我为你找到了这个:
Java编译器利用目标类型来推断a的类型参数 泛型方法调用。表达式的目标类型是Java编译器所期望的数据类型,具体取决于表达式的显示位置。考虑方法Collections.emptyList,声明如下:
static <T> List<T> emptyList();
考虑以下赋值语句:
List<String> listOne = Collections.emptyList();
此语句期待List的实例;此数据类型是目标类型。因为方法emptyList返回List类型的值,所以编译器推断类型参数T必须是值String。这适用于Java SE 7和8.或者,您可以使用类型见证并指定T的值,如下所示:
List<String> listOne = Collections.<String>emptyList();
但是,在这种情况下,这不是必需的。不过,在其他情况下这是必要的。请考虑以下方法:
void processStringList(List<String> stringList) {
// process stringList
}
假设您要使用空列表调用方法processStringList。在Java SE 7中,以下语句不编译:
processStringList(Collections.emptyList());
Java SE 7编译器生成类似于以下内容的错误消息:
List<Object> cannot be converted to List<String>
编译器需要类型参数T的值,因此它以值Object开头。因此,Collections.emptyList的调用返回List类型的值,该值与方法processStringList不兼容。因此,在Java SE 7中,您必须指定type参数值的值,如下所示:
processStringList(Collections.<String>emptyList());
Java SE 8中不再需要这个。目标类型的概念已经扩展为包含方法参数,例如方法processStringList的参数。在这种情况下,processStringList需要一个List类型的参数。方法Collections.emptyList返回List的值,因此使用List的目标类型,编译器推断类型参数T的值为String。因此,在Java SE 8中,以下语句编译:
processStringList(Collections.emptyList());
有关详细信息,请参阅Lambda表达式中的目标类型。
阅读this
答案 1 :(得分:4)
它可以通过类型推断来实现。
简单来说,您可以进行如下任务:
List<String> list = newArrayList();
其中newArrayList()
可能是:
static <E> List<E> newArrayList() {
return new ArrayList<>();
}
这里编译器需要确定E
类型变量的类型。为此,它将使用作业的左侧,其中包含List<String>
,因此E == String
。现在它知道了类型变量,一切都没问题。
与使用番石榴进行如此简单的任务相反,我建议你简单地写一下:
List<String> list = new ArrayList<>();
我会称之为更清洁,不需要(不必要的)外部依赖。
答案 2 :(得分:2)
简短回答是Type Inference。
这是发生了什么。你不是简单地打电话给Lists.newArrayList()
;你也将它分配给一个变量,比如
List<Person> people = Lists.newArrayList();
由于您已在变量声明中指定了类型参数,因此Java能够“推断”(计算出)您在新ArrayList中需要的类型参数,因此您无需指定它们。 / p>
答案 3 :(得分:0)
编译器从变量/方法声明中推断出类型。 见http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html