我正在从here阅读Java教程。我在理解一条简单的线时遇到了麻烦。
教程说Collections.emptyList
的声明是:
static <T> List<T> emptyList();
因此,如果我们编写List<String> listOne = Collections.emptyList();
,则它的工作原理是Java编译器能够推断出type参数,因为返回值应该是List<String>
类型。
现在考虑一种方法:void processStringList(List<String> stringList)
。现在它说:
processStringList(Collections.emptyList()); Java SE 7编译器 生成类似于以下内容的错误消息:
列表<'对象>不能转换为列表<'字符串>
编译器要求 类型参数T的值,因此它以值Object开头。 因此,调用Collections.emptyList返回一个值 类型为
List<Object>
的类型,与方法不兼容 processStringList
现在它们是什么意思:所以它以值Object 开头?我的意思是开始做什么?
答案 0 :(得分:4)
基本上,这是关于编译器的功能的。换句话说:在某种程度上,可能类型推断的“数量”是实现细节。
在Java 7中,有时必须使用类型helper / hints / witnesses,您可以在其中Collections.<String>emptyList()
告诉编译器该缺失的部分。
以后的编译器实现改善了您几乎总是可以使用Collections.emptyList()
的情况。
关于The compiler requires a value for the type argument T so it starts with the value Object.
...实际上非常简单:java编译器必须实现一种算法,最终可以推断出特定类型。提供一些伪代码,可能类似于:
Class<?> inferType(SomeSyntaxTree construct) {
我在这里仅使用Class
来表示该算法将返回类似于已知类型的内容。现在,该方法 可以这样实现:
Class<?> inferedType = Object.class
while (whatever) {
refine inferedType
}
return inferedType
换句话说:当您“搜索”某些值时,这是一种非常常见的方法:使用“最通用”值进行初始化(在Java类型系统中,将是Object.class),然后您会看到是否可以通过应用任何算法来完善该通用值。
在我们的情况下,优化可能会导致最终得出“可以使用的最特定类型为String
”,但是如果无法进一步优化,则最终会出现“初始默认值”,是Object
。
答案 1 :(得分:1)
声明
processStringList(Collections.emptyList());
在Java 8中工作正常(我也假设在8以上)。在这种情况下,编译器足够聪明,可以通过检查该方法的预期参数类型来推断类型。
在较早的版本中,当编译器没有看到显式的返回类型时(如List<String> listOne = Collections.emptyList();
),默认情况下,它将推断<T>
至java.lang.Object
。但是请注意,List<Object>
和List<String>
不兼容。
您可以声明类似void processString(List<? super String> list)
的方法来避免该错误。