我最近遇到了这种不同寻常的(对我而言)Java语法...这是一个例子:
List list = new <String, Long>ArrayList();
请注意<String, Long>
类型参数的定位...它不是在正常类型之后但在之前。我不介意承认我之前从未见过这种语法。另请注意,当ArrayList
只有1时,有2个类型参数。
类型参数的定位与将它们放在类型后的含义相同吗?如果不是,那么不同的定位意味着什么?
当ArrayList
只有1时,为什么有2个类型参数是合法的?
我搜索了常见的地方,例如。 Angelika Langer在这里除了ANTLR项目中Java语法文件中的语法规则之外,在任何地方都找不到这种语法。
这是不寻常的,但完全有效的Java。要理解我们需要知道一个类可能有一个泛型构造函数,例如:
public class TypeWithGenericConstructor {
public <T> TypeWithGenericConstructor(T arg) {
// TODO Auto-generated constructor stub
}
}
我想通常在通过泛型构造函数实例化类时,我们不需要将类型参数显式化。例如:
new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
现在T
显然是LocalDate
。但是,可能存在Java无法推断(推断)类型参数的情况。然后我们使用您问题中的语法显式提供它:
new <LocalDate>TypeWithGenericConstructor(null);
当然,即使我们认为它有助于提高可读性或出于任何原因,我们也可以提供它:
new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
在你的问题中,你似乎在调用java.util.ArrayList
构造函数。那个构造函数不是通用的(只有ArrayList
类作为一个整体,这是其他的东西)。为什么Java允许您在不使用时在调用中提供类型参数,请参阅下面的编辑。我的Eclipse给了我一个警告:
未使用的类型参数,用于ArrayList类型的非泛型构造函数ArrayList();它不应该用参数参数化
但这不是一个错误,程序运行良好(我还得到关于List
和ArrayList
缺少类型参数的警告,但这又是一个不同的故事)。
类型参数的定位与将它们放在类型后的含义相同吗?如果不是,那么不同的定位意味着什么?
不,这是不同的。类型(ArrayList<Integer>()
)之后的通常类型参数/ s用于泛型类。之前的类型参数是构造函数。
这两种形式也可以组合在一起:
List<Integer> list = new <String, Long>ArrayList<Integer>();
我会认为这更正确,因为我们现在可以看到列表存储Integer
对象(当然我仍然宁愿忽略无意义的<String, Long>
)。
当ArrayList只有1时,为什么有2个类型参数是合法的?
首先,如果在类型之前提供类型参数,则应该为构造函数提供正确的数字,而不是类,因此它与ArrayList
类已经有多少类型参数没有任何关系。这实际上意味着在这种情况下你不应该提供任何,因为构造函数不接受类型参数(它不是通用的)。无论如何,当你提供一些东西时,它们会被忽略,这就是为什么你供应的数量和数量无关紧要的原因。
编辑感谢@Slaw的链接:Java允许所有方法调用的类型参数。如果被调用的方法是通用的,则使用类型参数;如果没有,他们会被忽略。例如:
int length = "My string".<List>length();
是的,这很荒谬。 Java语言规范(JLS)在第15.12.2.1小节中给出了这个理由:
该规则源于兼容性和可替代性原则的问题。由于接口或超类可以独立于其子类型进行泛化,因此我们可以使用非泛型方法覆盖泛型方法。但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用。否则,子类型不能替代其生成的超类型。
该参数不适用于构造函数,因为它们不能直接覆盖。但我想他们想要有相同的规则,以免使已经很复杂的规则过于复杂。在任何情况下,关于实例化和new
的第15.9.3节不止一次是指15.12.2。
显然,您可以使用您喜欢的任何通用参数为任何非泛型方法/构造函数添加前缀:
new <Long>String();
Thread.currentThread().<Long>getName();
编译器不关心,因为它不必将这些类型参数与实际泛型参数匹配。
一旦编译器必须检查参数,它就会抱怨不匹配:
Collections.<String, Long>singleton("A"); // does not compile
对我来说似乎是一个编译器错误。