构造函数类型参数在放置*之前的类型是什么意思?

问题描述 投票:123回答:2

我最近遇到了这种不同寻常的(对我而言)Java语法...这是一个例子:

List list = new <String, Long>ArrayList();

请注意<String, Long>类型参数的定位...它不是在正常类型之后但在之前。我不介意承认我之前从未见过这种语法。另请注意,当ArrayList只有1时,有2个类型参数。

类型参数的定位与将它们放在类型后的含义相同吗?如果不是,那么不同的定位意味着什么?

ArrayList只有1时,为什么有2个类型参数是合法的?

我搜索了常见的地方,例如。 Angelika Langer在这里除了ANTLR项目中Java语法文件中的语法规则之外,在任何地方都找不到这种语法。

java generics syntax constructor generic-type-argument
2个回答
138
投票

调用泛型构造函数

这是不寻常的,但完全有效的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();它不应该用参数参数化

但这不是一个错误,程序运行良好(我还得到关于ListArrayList缺少类型参数的警告,但这又是一个不同的故事)。

泛型类与泛型构造函数

类型参数的定位与将它们放在类型后的含义相同吗?如果不是,那么不同的定位意味着什么?

不,这是不同的。类型(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。

链接


0
投票

显然,您可以使用您喜欢的任何通用参数为任何非泛型方法/构造函数添加前缀:

new <Long>String();
Thread.currentThread().<Long>getName();

编译器不关心,因为它不必将这些类型参数与实际泛型参数匹配。

一旦编译器必须检查参数,它就会抱怨不匹配:

Collections.<String, Long>singleton("A"); // does not compile

对我来说似乎是一个编译器错误。

© www.soinside.com 2019 - 2024. All rights reserved.