Eclipse / javac在使用默认方法冲突编译签名时不同意;谁是对的? [英] Eclipse/javac disagree on compiling signature with default method collision; who is right?

查看:132
本文介绍了Eclipse / javac在使用默认方法冲突编译签名时不同意;谁是对的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里有一个简单的类来演示这个问题:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆