构造器类型参数放在类型“之前”是什么意思?

时间:2019-03-25 02:55:05

标签: java generics syntax constructor generic-type-argument

我最近遇到了这种不寻常的(对我来说)Java语法...这是一个示例:

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

请注意<String, Long>类型参数的位置...它不是在普通类型之后,而是之前。我不介意承认我以前从未见过这种语法。另请注意,ArrayList仅具有1时有2个类型参数。

类型参数的位置是否与将其放在类型后面的含义相同?如果不是,那么不同的定位是什么意思?

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

我搜索了平常的地方,例如。 Angelika Langer等等,但是在ANTLR项目的Java语法文件中的语法规则之外,找不到任何关于此语法的提及。

2 个答案:

答案 0 :(得分:137)

调用通用构造函数

这是不寻常的,但是完全有效的Java。要理解,我们需要知道一个类可能具有通用构造函数,例如:

public class TypeWithGenericConstructor {

    public <T> TypeWithGenericConstructor(T arg) {
        // TODO Auto-generated constructor stub
    }

}

我想通过泛型构造函数实例化类时,我们通常不需要显式地声明类型参数。例如:

    new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

现在T很明显是LocalDate。但是,在某些情况下,Java无法推断(推导)type参数。然后,使用您提出的问题的语法来明确提供它:

    new <LocalDate>TypeWithGenericConstructor(null);

我们当然也可以提供它,即使我们认为它有助于提高可读性或出于任何原因也没必要:

    new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

在您的问题中,您似乎正在调用java.util.ArrayList构造函数。该构造函数不是通用的(整体上只有ArrayList类,仅此而已)。关于为什么Java允许您在不使用类型参数时在调用中提供它们的原因,请参见下面的编辑。我的Eclipse给了我警告

  

非通用构造函数ArrayList()的未使用类型参数   类型ArrayList;不应使用参数

对其进行参数化

但这不是错误,程序运行正常(我还收到有关ListArrayList缺少类型参数的警告,但这又是另外一回事了。

泛型类与泛型构造器

  

类型参数的位置是否与   将它们放在类型之后?如果没有,有什么不同   定位是什么意思?

不,这是不同的。类型(ArrayList<Integer>()之后的 后通常是类型参数,用于泛型类之前的类型参数用于构造函数

这两种形式也可以组合在一起:

    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。

链接

答案 1 :(得分:0)

显然,您可以为任何非泛型方法/构造函数加上所需的任何泛型参数前缀:

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

编译器不在乎,因为它不必将这些类型的参数与实际的泛型参数进行匹配。

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

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

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