当前位置: 首页 > 知识库问答 >
问题:

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

孟豪
2023-03-14

我最近遇到了这种不同寻常的Java语法。。。下面是一个例子:

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

注意

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

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

我已经搜索了常见的地方,例如Angelika Langer和这里的其他地方,但是除了ANTLR项目的Java语法文件中的语法规则之外,在任何地方都找不到任何关于这种语法的提及。


共有2个答案

汝承载
2023-03-14

显然,您可以在任何非泛型方法/构造函数前面加上您喜欢的任何泛型参数:

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

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

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

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

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

郎雅昶
2023-03-14

这是不寻常的好吧,但完全有效的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允许您在调用中不使用类型参数时提供它们,请参阅下面的编辑。我的日蚀给了我一个警告:

ArrayList类型的非泛型构造函数ArrayList()的未使用类型参数;它不应该用参数参数化

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

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

不,不一样。通常类型参数后的类型(ArrayList

这两种形式也可以合并:

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

我认为这一点更正确,因为我们现在可以看到列表存储了整数对象(我仍然倾向于忽略无意义的代码)

当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。

  • CodesJava上的泛型构造函数
  • JLS 15.9.3。选择构造函数及其参数
  • JLS 15.12.2.1。确定可能适用的方法
  • 在所有方法调用上允许类型见证有什么意义

 类似资料:
  • 问题内容: 代码是什么 在构造函数内部吗? 例如,这是我的课程的构造函数 它和超类有关吗? 问题答案: 它调用父类的构造函数

  • 问题内容: 这些来自github上的spring amqp示例,位于https://github.com/SpringSource/spring-amqp- samples.git 这些是 什么类型的Java构造函数?它们是吸气剂和吸气剂的捷径吗? 与此相反 问题答案: 这些构造函数被重载以使用调用另一个构造函数。第一个无参数构造函数使用空参数调用第二个。第二呼叫的第三构造(未示出),其必须采取,

  • 问题内容: 该链接指出以下内容: 具有实际类型参数的泛型类型的实例化称为参数化类型。示例(参数化类型): 那么什么是参数化类型? 要么 问题答案: 它们都是参数化类型:以其他类型为参数的类型。 您在表达式的两侧具有不同类型的事实是无关紧要的,并且与多态行为有关,即因为是的子类型。

  • 问题内容: 所以我正在学习Java。我有一个月的时间,我刚刚了解了构造函数。但是我看不到创建一个的全部目的。为什么我什么时候要使用?我知道它没有主方法,您可以从主类中调用构造函数。任何人都可以启发我这个话题,这对我有很大帮助。 问题答案: 构造函数是用来初始化/设置类实例的对象。 如果您有一个对象需要一些处理才能使用(例如,初始化成员),则应在构造函数中执行此操作。 理想情况下,您永远不应拥有“部

  • 问题内容: 我想知道Eclipse中的错误消息是什么意思: 构造函数Case(问题,解决方案,double,CaseSource)含糊不清 问题答案: 当您尝试实例化一个可应用于多个构造函数的类时,将存在问题。 例如: 如果用String对象调用构造函数,则有一个确定的构造函数。但是,如果实例化,则它可能适用于任何一个,因此是不明确的。 具有相似签名的方法同样适用。

  • 问题内容: 试图屈服于Javascript对OO的追求,并且像其他许多人一样,对该属性感到困惑。特别是,属性的重要性,因为我似乎无法使其发挥任何作用。例如: 在上面的示例中,该对象似乎具有名为()的正确构造函数-并且它继承了的age属性。那么为什么很多人建议将此作为必要步骤: 显然,在构造 时 调用了正确的构造函数,那么这个原型属性有什么影响?我很想知道“正确设置”构造函数的属性会对实际产生什么影