Eclipse / javac在使用默认方法冲突编译签名时不同意;谁是对的? [英] Eclipse/javac disagree on compiling signature with default method collision; who is right?
问题描述
这里有一个简单的类来演示这个问题:
package com.mimvista.debug;
和
public class DefaultCollisionTest {
public static interface Interface1 {
public String getName();
}
public static interface Interface2 {
public default String getName(){returnMr. 2; };
}
public static< X extends Interface1& Interface2> String extractName(X target){
return target.getName();
Eclipse(Neon 2)很高兴地编译这个类,而javac (JDK 1.8.0_121)吐出以下编译错误:
$ javac src / com / mimvista / debug / DefaultCollisionTest.java
src\com\mimvista\debug\DefaultCollisionTest.java:13:error:class INT#1从类型Interface2和Interface1继承了getName()的抽象和默认值
public static< X扩展Interface1& Interface2>字符串extractName(X目标){
^
其中INT#1是交叉类型:
INT#1扩展Object,Interface1,Interface2
1错误
我相信Eclipse在这种情况下是正确的,但我并不完全确定。根据我对继承抽象和默认错误的理解,我认为它应该只在编译实现这两个接口的实际声明类时生成。看起来像javac可能会生成一个中间类来处理这个通用签名,并将其错误地进行默认的方法碰撞测试?
解决方案在 Java Bug数据库中进行报告: JDK-8186643
通过史蒂芬赫尔曼(见):
lockquote
右,只有在交叉点类型发生错误时才报告错误截面类型不完整,因此交叉点是空的。但正如这个答案所示,交叉点不是空的,因此应该是合法的。实际上,错误消息
类INT#1继承...
是没有意义的,因为在那时没有人提到 class INT#1,所以我们只有两个接口的交集,并且该交集仅用作边界,而不是作为类型。
实现同一方法的多个接口的类可以用两种编译器编译,即使一个接口的方法具有默认实现。该类可以被引用为
< T extends I1& I2>
,只要 I1 和 I2 对同名命名方法都没有默认实现。只有两个接口中的一个具有默认实现时 javac 才会失败。
如果含糊不清在定义一个类时应该已经发生错误,而不是当类被称为
< T extends ...>
时(参见JLS 4.9。相交类型 I2>< T extends IDefault>
,但是失败并且< T extends I1& IDefault>
和 javac :
接口I1 {
String get();
}
interface I2 {
String get();
}
interface IDefault {
default String get(){
returndefault;
};
}
public class Foo实现I1,I2,IDefault {
@Override
public String get(){
returnfoo ;
public static void main(String [] args){
System.out.print(getOf(new Foo()));
}
// static< T extends I1& IDefault> String getOf(T t){//失败,返回javac
static< T extends I1& I2> String getOf(T t){// OK
return t.get();
}
}
Here's a simple class that demonstrates the issue:
package com.mimvista.debug; public class DefaultCollisionTest { public static interface Interface1 { public String getName(); } public static interface Interface2 { public default String getName() { return "Mr. 2"; }; } public static <X extends Interface1&Interface2> String extractName(X target) { return target.getName(); } }
Eclipse (Neon 2) happily compiles this class while javac (JDK 1.8.0_121) spits out the following compile error:
$ javac src/com/mimvista/debug/DefaultCollisionTest.java src\com\mimvista\debug\DefaultCollisionTest.java:13: error: class INT#1 inherits abstract and default for getName() from types Interface2 and Interface1 public static <X extends Interface1&Interface2> String extractName(X target) { ^ where INT#1 is an intersection type: INT#1 extends Object,Interface1,Interface2 1 error
I believe that Eclipse is correct in this case but I'm not totally sure. Based on my understanding of the "inherits abstract and default" error, I think it should only be generated when compiling an actual declared class that implements those two interfaces. It seems like javac may be generating an intermediate class under-the-hood to deal with that generic signature and erroneously subjecting it to the default method collision test?
解决方案Eclipse is right.
I have not found this javac bug in the Java Bug Database and therefore reported it: JDK-8186643
Better explanation by Stephan Herrmann (see his comment below):
Right, reporting an error against an intersection type should only happen when the intersection type is not well-formed and hence the intersection is empty. But as this answer shows, the intersection is not empty and should thus be legal. Actually, the error message
class INT#1 inherits ...
makes no sense, because at that point nobody mentioned a class INT#1, we only have the intersection of two interfaces, and that intersection is used only as a bound, not as a type.A class that implements multiple interfaces of the same method can be compiled with both compilers, even if the method of one interface has a default implementation. The class can be referenced as
<T extends I1 & I2>
as long as neither I1 nor I2 has a default implementation for a equally named method. Only if one of the two interfaces has a default implementation javac fails.In case of ambiguity which implementation should apply, the error should already occur when defining a class, not when the class is referred as
<T extends ...>
(see JLS 4.9. Intersection Types).See following example which works with
<T extends I1 & I2>
and<T extends IDefault>
, but fails with<T extends I1 & IDefault>
and javac:interface I1 { String get(); } interface I2 { String get(); } interface IDefault { default String get() { return "default"; }; } public class Foo implements I1, I2, IDefault { @Override public String get() { return "foo"; } public static void main(String[] args) { System.out.print(getOf(new Foo())); } // static <T extends I1 & IDefault> String getOf(T t) { // fails with javac static <T extends I1 & I2> String getOf(T t) { // OK return t.get(); } }
这篇关于Eclipse / javac在使用默认方法冲突编译签名时不同意;谁是对的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!