繁体   English   中英

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

[英]What do constructor type arguments mean when placed *before* the type?

我最近遇到了这种不同寻常的(对我而言)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(); 它不应该用参数参数化

但这不是一个错误,程序运行正常(我另外得到关于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小节中给出了这个理由:

该规则源于兼容性和可替代性原则的问题。 由于接口或超类可以独立于其子类型进行泛化,因此我们可以使用非泛型方法覆盖泛型方法。 但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用。 否则,子类型不能替代其生成的超类型。

该参数不适用于构造函数,因为它们不能直接覆盖。 但我想他们想要有相同的规则,以免使已经很复杂的规则过于复杂。 无论如何,第15.9.3节关于实例化和new不止一次是指15.12.2。

链接

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM