与Java方法调用中的类型参数相关的问题
Java 8 Java语言规范提供了一个带有类型参数的方法调用示例,例如“示例4.11-1。类型的用法”:
void loop(S s) { this.loop(s); // is the the type argument for the method call. }
在该示例中,提供的类型参数是有意义的,但显然方法调用的类型参数也可能是多余的并且完全没有意义,甚至不需要涉及generics。 例如:
void m() { } void test() { m(); this.m(); this.m(); // Compiles and runs OK! this.m(); // Compiles and runs OK! m(); // Won't compile: "illegal start of expression" }
我有几个问题出现:
-
任何人都可以建议Java允许这些冗余类型参数的正当理由吗? 接受它们没有伤害,我似乎仍然喜欢编译器能够并且应该捕获的东西。
-
如果使用类型参数调用方法前缀为“this”,则该代码仅编译。 否则你会得到“非法开始表达”错误。 那是一个错误吗? 不应该使用任何明确的方法调用“this”。 没有“这个”也能工作吗?
(这些问题的催化剂是Oracle对我为一个有趣的Java问题创建的错误报告的回应 。
更新2015年9月18日
- 我在Oracle上针对这个问题提出了错误JDK-8098556 。 以下是他们的回复:
这不是问题 ; 使用与普通方法相同的规则检查方法引用 – 请注意,对于普通方法,您始终可以提供冗余类型参数:
void m() { } this.m(); //legal
方法(和构造函数)引用只是inheritance了这种行为,按照15.13:“”如果方法引用表达式的forms为ReferenceType :: [TypeArguments] Identifier,则可能适用的方法是要搜索的类型的成员方法name(由Identifier提供),accessibility,arity(n或n-1)和type argument arity(派生自[TypeArguments]),如§15.12.2.1中所述。“
- 由于该回复确认了TAsk已经在下面提供的信息(包括引用JLS的相关部分),我已经接受了这个答案。
以下是方法调用的方法:
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.m();//Also compiles }
除此之外,您将收到以下警告:
非generics方法m()的未使用类型参数…
以下是JLS 15.12.2.1的以下陈述,
此子句意味着非generics方法可能适用于提供显式类型参数的调用。 实际上,它可能适用。 在这种情况下,类型参数将被忽略 。
它确实说,它可能适用 (在运行时)
此外,
该规则源于兼容性和可替代性原则的问题。 由于接口或超类可以独立于其子类型进行生成,因此我们可以使用非generics方法覆盖generics方法。 但是,重写(非generics)方法必须适用于对generics方法的调用,包括显式传递类型参数的调用。 否则,子类型不能替代其生成的超类型。