【问题标题】:Issues relating to type parameters in Java method callsJava 方法调用中与类型参数有关的问题
【发布时间】:2015-08-28 00:46:36
【问题描述】:

Java 8 的 Java 语言规范在“示例 4.11-1. 类型的用法”中提供了一个带有类型参数的方法调用示例:

<S> void loop(S s) {
    this.<S>loop(s); // <S> is the the type argument for the method call.
}

在该示例中,提供的类型参数是有意义的,但显然方法调用的类型参数也可能是多余的并且完全没有意义,甚至不需要涉及泛型。例如:

void m() { }

void test() {
    m(); 
    this.m(); 
    this.<Integer>m(); // Compiles and runs OK!
    this.<String, Byte, StringBuilder, Thread[], Throwable>m(); // Compiles and runs OK!
    <Integer>m(); // Won't compile: "illegal start of expression"
}

我有几个问题:

  1. 谁能建议 Java 允许这些冗余类型参数的正当理由?接受它们没有害处,在我看来它仍然是编译器可以并且应该捕获的东西。

  2. 仅当使用类型参数的方法调用以“this.”为前缀时,该代码才会编译。否则你会得到“非法的表达式开始”错误。那是一个错误吗?不应该有任何与“this”一起使用的明确方法调用。没有“this”也可以工作。

(这些问题的催化剂是 Oracle's response 我为一个有趣的 Java 问题创建的错误报告,有人在 SO 上提出了 here。)

2015 年 9 月 18 日更新

  1. 我针对这个问题向 Oracle 提出了 bug JDK-8098556。以下是他们的回应:

这不是问题;使用与普通方法相同的规则检查方法引用 - 请注意,对于普通方法,您始终可以提供冗余类型参数:

void m() { }
this.<String>m(); //legal

方法(和构造函数)引用仅继承此行为,根据 15.13: ""如果方法引用表达式的格式为 ReferenceType :: [TypeArguments] Identifier,则可能适用的方法是要搜索的类型的成员方法,它们具有适当的名称(由 Identifier 给出)、可访问性、arity(n 或 n- 1) 和类型参数 arity(派生自 [TypeArguments]),如 §15.12.2.1 中所述。”

  1. 由于该回复确认了 TAsk 已在下面提供的信息(包括引用 JLS 的相关部分),因此我接受了该回复。

【问题讨论】:

  • 不知道为什么它是允许的,但在 Eclipse 中你会得到一个警告“未使用类型参数的非泛型方法 m() 类型的测试;它不应该用参数 参数化”,这意味着不要这样做。 --- this.&lt;Integer&gt;m() 在语法上 是有效的,并且在输入 m 时有意义,但 &lt;Integer&gt;m() 是完全无效的语法。
  • 第二个问题一定是因为一些解析/语法困难。例如,x&lt;&lt;Integer&gt;m()
  • @bayou.io 您的链接非常相​​关。 Gábor Bakos 给出的关于为什么允许“无效”使用类型参数的答案有些说服力,尽管它解决了一种非常罕见的情况。
  • @skomisa - 我认为 Bakos 的回答完全没有根据。从未有过与非泛化进行可比性的目标。

标签: java java-8


【解决方案1】:

以下是方法调用的方式

JLS 15.12列出了以下调用方法的方式,

  • MethodName ( [ArgumentList] ) (注意:这里没有类型参数)
  • TypeName.[TypeArguments] Identifier ( [ArgumentList] )
  • ExpressionName.[TypeArguments] Identifier ( [ArgumentList] )
  • Primary.[TypeArguments] Identifier ( [ArgumentList] )
  • super.[TypeArguments] Identifier ( [ArgumentList] )
  • TypeName.super.[TypeArguments] Identifier ( [ArgumentList] )

因此,在 Java 语言规范中,具有表达式或类型名称的方法可以具有类型参数,但请注意,在第一个方法调用中您不能指定类型参数,因为它是非法的。

请注意,只有this 不允许这样做,但static 调用和super 方法调用也可以有类型参数,这些都是完全合法的。

static void m() { }

void test() {
   ClassName.<Integer>m();//Also compiles
}

除此之外,您将收到以下警告,

非泛型方法 m() 的未使用类型参数 ...

代表JLS 15.12.2.1的以下语句,

该子句暗示非泛型方法可能是potentially 适用于提供显式类型参数的调用。 事实上,它可能会被证明是适用的。在这种情况下,类型 参数将被忽略

它说确实,它可能证明是适用的(在运行时)

此外,

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

【讨论】:

  • 您对 JLS15.12.2.1 的引用肯定指出了为什么可以容忍/忽略类型参数,尽管在不使用泛型的情况下这似乎有点牵强,并且被调用的方法没有参数。 Andreas 在上面的评论中指出 Eclipse 至少给出了编译器警告。我使用的是 Oracle JDK 8u60,它根本没有发出任何警告。
猜你喜欢
  • 1970-01-01
  • 2019-07-09
  • 1970-01-01
  • 2016-10-21
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
相关资源
最近更新 更多