Java 7中接口方法的注解会被继承,但在Java 8中不会。

18

我正在将代码从Java 7迁移到Java 8,遇到了这个语言上的变化。

我有一个具有注解方法的超级接口:

public interface SuperInterface {

  @X
  SuperInterface getSomething();
}

我有一个带有相同注释方法的SubInterface,但返回的是一个子接口:

public interface SubInterface extends SuperInterface {

  @X
  SubInterface getSomething();
}

当我运行这个测试时,Java 8会失败,但Java 7不会:

import java.lang.reflect.Method;

public class Test {

  public static void main(String[] args) {
    final Method[] methods = SubInterface.class.getMethods();
    for (Method method : methods) {
      if (method.getAnnotations().length == 0) {
        throw new RuntimeException("No annotations found for " + method);
      }
    }
  }
}

Java 7中,接口方法的注解是可以被继承的,但在Java 8中不再如此,这是真的吗?

@X被定义为:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface X {  
}

2
为什么返回SuperInterface的一个方法是default方法?在Java7中,它们都是抽象的,但在Java8中,其中一个是default方法。 - Shadov
1
@Shadov 是的,在Java 8中:public default SuperInterface test.SubInterface.getSomething(),但在Java 7中:public abstract test.SuperInterface SuperInterface.getSomething()。 - leaqui
3
@Shadov:这在《Java 7与Java 8中getDeclaredMethods()的行为差异》中有解释。 - Holger
@Holger 我的意思是继承的接口方法在子接口中不会保留自己的注释。 - leaqui
7
实际上,你正在覆盖SubInterface中的getSomething()方法,因此没有继承的方法。这是Java 8之前的技术缺陷,在Reflection中可以看到SuperInterface的擦除方法。在Java 8中(使用javac),您不会看到任何继承的方法,而是看到两个方法,即覆盖方法和桥接方法。桥接方法将具有覆盖方法的注释,但不会继承SuperInterface.getSomething()方法的注释。 - Holger
显示剩余3条评论
2个回答

21
据我所知,根据这里的说法,在Java-8中至少build 94版本应该可以正常工作。因此,这是一个eclipse编译器的错误(我无法使用javac复现它)。在这里您使用协变,因此会生成两种方法(其中一种是桥接方法):
 for (Method method : methods) {
        if (method.getAnnotations().length == 0) {
            System.out.println("Not present " + method.getName() + " isBridge? " + method.isBridge());
        } else {
            System.out.println("Present :" + method.getName() + " isBridge? " + method.isBridge());
        }
    }

但是这样应该可以工作,因为错误明确指出:带有运行时保留的注释应该由javac复制到桥接方法中

javac的输出:

Present :getSomething isBridge? false
Present :getSomething isBridge? true

使用 eclipse 编译器 的输出:

Present :getSomething isBridge? false
Not present getSomething isBridge? true

2
天啊,我测试了从beta132到update121之间的所有JDK版本,得出结论无法复现,但我应该在beta94之前测试版本... - Holger
3
@Holger 你在机器上安装了从beta132到update121的所有JDK版本?这真是太棒了! - Eugene
3
也许“全部”这个词有点委婉了。我拥有所有相关更新,可以得出结论,通过这些更新的代码也能与未发布的中间版本一起使用。更新编号系统使其看起来比实际情况要复杂得多。更有趣的部分是脚本,它们会迭代所有更新来编译和运行测试程序... - Holger
3
@Holger,那是我能想到的唯一事情!脚本!!!我立刻想着要尝试为自己创建类似的东西。 - Eugene

10
对于Eclipse ecj编译器而言,这看起来像是Eclipse bug 495396,它引用了JDK 6695379。
它被标记为针对4.7版本,但4.7版本已经处于发布候选状态,所以我猜它没有被包含进去。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接