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

Nat*_*ams 126 java generics syntax constructor generic-type-argument

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

List list = new <String, Long>ArrayList();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Ole*_*.V. 141

调用通用构造函数

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

public class TypeWithGenericConstructor {

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

}
Run Code Online (Sandbox Code Playgroud)

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

    new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
Run Code Online (Sandbox Code Playgroud)

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

    new <LocalDate>TypeWithGenericConstructor(null);
Run Code Online (Sandbox Code Playgroud)

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

    new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));
Run Code Online (Sandbox Code Playgroud)

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

ArrayList类型的非泛型构造函数ArrayList()的未使用类型参数;不应使用参数对其进行参数化

但它不是一个错误,并且该程序运行正常(我还得到警告有关缺失类型参数ListArrayList,但是这又是一个不同的故事)。

泛型类与泛型构造函数

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

不,不一样 type()之后的通常类型参数ArrayList<Integer>()是针对泛型类的之前的类型参数用于构造函数

两种形式也可以结合使用:

    List<Integer> list = new <String, Long>ArrayList<Integer>();
Run Code Online (Sandbox Code Playgroud)

我认为这会更正确,因为我们现在可以看到列表存储了Integer对象(当然,我仍然希望忽略无意义<String, Long>的对象)。

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

首先,如果在类型之前提供类型实参,则应该为构造函数而不是为类提供正确的数字,因此与ArrayList该类有多少个类型实参没有任何关系。这实际上意味着在这种情况下您不应该提供任何内容,因为构造函数不接受类型参数(这不是通用的)。无论如何,当您提供一些东西时,它们都会被忽略,这就是为什么无论您提供多少根都无关紧要。

为什么允许无意义的类型参数?

感谢@Slaw进行链接编辑:Java允许在所有方法调用上使用类型参数。如果被调用的方法是通用的,则使用类型参数。如果不是,它们将被忽略。例如:

    int length = "My string".<List>length();
Run Code Online (Sandbox Code Playgroud)

是的,这很荒谬。Java语言规范(JLS)在第15.12.2.1小节中提供了这一理由:

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

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

链接

  • 嗯...可能不是错误。参见/sf/ask/1961039741/ (4认同)
  • 对我来说有趣的是,如果我想念您已经提到过/暗含了这一点,对不起,是如果构造函数(或方法)_is_泛型,类型参数的数量不正确,则会导致编译错误-至少对于我来说在OpenJDK 12中有效。例如,如果您具有类Test {&lt;T&gt; Test(){}}`,则调用`new &lt;String,Long&gt; Test()`是错误的。但是,当构造函数(或方法)不是通用类时,它可以让您将类型参数添加到您的心中……也许这应该被认为是一个错误(如果不在编译器中,那么在规范中)? (3认同)