当前位置: 首页 > 面试题库 >

为什么AnnotationPresent在Java 7和Java 8之间的工作方式不同?

唐焕
2023-03-14
问题内容

今天,我刚刚发现了一个问题,因为我的一个单元测试由于从Java 7升级到Java
8而失败了。该单元测试调用一个方法,该方法试图在子类上注释的方法上找到注释,但返回类型不同。

在Java 7中,isAnnotationPresent似乎只有在代码中确实声明了注释时,才会找到注释。在Java
8中,isAnnotationPresent似乎包含在子类中声明的注释。

为了说明这一点,我创建了一个简单的(??)测试类IAPTest(用于IsAnnotationPresentTest)。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

public class IAPTest {
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Anno {
    }
    public static interface I {
    }
    public static interface IE extends I {
    }
    public static class A {
        protected I method() {
            return null;
        }
    }
    public static class B extends A {
        @Anno
        protected IE method() {
            return null;
        }
    }
    public static void main(String[] args) {
        for (Method method : B.class.getDeclaredMethods()) {
            if (method.getName().equals("method") && I.class.equals(method.getReturnType())) {
                System.out.println(method.isAnnotationPresent(Anno.class));
            }
        }
    }
}

在最新的Java 7(撰写本文时为1.7.0_79)上,此方法显示“ false”。在最新的Java 8(撰写本文时为1.8.0_66)上,此方法显示“
true”。我直觉上希望它打印“ false”。

为什么是这样?这是否表示Java中的错误或Java工作方式的预期变化?

编辑 :只是为了显示我用来复制它的确切命令(在与上面的代码块相同的IAPTest.java目录中):

C:\test-isannotationpresent>del *.class

C:\test-isannotationpresent>set JAVA_HOME=C:\nma\Toolsets\AJB1\OracleJDK\jdk1.8.0_66

C:\test-isannotationpresent>set PATH=%PATH%;C:\nma\Toolsets\AJB1\OracleJDK\jdk1.8.0_66\bin

C:\test-isannotationpresent>java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

C:\test-isannotationpresent>javac IAPTest.java

C:\test-isannotationpresent>java IAPTest
true

C:\test-isannotationpresent>

问题答案:

我相信这与Java
8兼容性指南中
提到的更改有关

从此版本开始,参数和方法注释将复制到合成桥方法。此修复程序意味着现在对于以下程序:

@Target(value = {ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) @interface ParamAnnotation {}  
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) @interface MethodAnnotation {}  
abstract class T<A,B> {
    B m(A a){
        return null;
    }  
}  
class CovariantReturnType extends T<Integer, Integer> {
    @MethodAnnotation
    Integer m(@ParamAnnotation Integer i) {
        return i;
    }

    public class VisibilityChange extends CovariantReturnType {}  
}

每个生成的桥方法将具有其重定向到的方法的所有注释。参数注释也将被复制。行为的这种变化可能会影响某些注释处理器,或者通常会影响使用注释的任何应用程序

第二个返回an I而不是an
IE的方法是生成的合成方法,因为您覆盖的方法中的返回类型比父类中的返回类型窄。请注意,如果您没有缩小的返回类型,则它不在声明的方法列表中。所以我认为这不是错误,而是有意的更改。



 类似资料:
  • 我们有一个运行在java 7上的服务器端进程:java-version:java version“1.7.0”java(TM)SE运行时环境(build 1.7.0-b147)java HotSpot(TM)64位服务器VM(build 21.0-b17,混合模式) 它接受来自我们自己开发的java应用程序(通过正确签名的JNLP启动)的SSL连接。 通常情况下,不管客户机应用程序是运行在Java

  • 问题内容: For 输出,而for 一些地址输出。当我们想要输出值时,我们可以像使用一样使用它们。 我只是想知道为什么我们不能直接调用以获得值。这样做不是更直观,更方便吗?是什么导致对和的不同处理? 问题答案: 数组和数组列表之间的主要区别在于,数组列表是用Java编写的类,具有自己的实现(包括覆盖的决定),而数组是语言规范本身的一部分。JLS 10.7 特别指出: 数组类型的成员都是以下所有:

  • 问题内容: 我想知道子句中和值之间的区别。当我使用而不是时,为什么会给我一个编译错误(一个值可能尚未初始化)。 下面是我的代码: 问题答案: 简短答案 对于编译器,可以推断出在读取之前已初始化的内容。这并非如此。 正式答案: 在读取之前, 所有局部变量都必须有一个 确定的赋值 (14.4.2。局部变量声明的执行): […]如果声明符没有初始化表达式, 则对变量的每次引用都必须在对变量的赋值之前执行

  • 下面是示例代码。执行的结果不同于jdk@11和jdk@17.将BigDecimal(1000)格式化为字符串后,白色字符看起来有所不同。最后,结果看起来是一样的——但是,它不是相同的字符串(比较bytes和Base64.encoded)。 jdk@11的结果不同于jdk@17 jdk@11结果: jdk@17结果: jdk@11: jdk@17: 系统: 这是预期行为吗?

  • 问题内容: 当用于运行“ ”(仅作为示例)时,在Windows上我们得到以下信息: 因此,可以按照我的期望很好地打印出该版本。但是在Linux上,我们得到以下信息: 因为gcc尚未收到该选项。 文档没有明确指定Windows下的args应该发生什么,但是它确实说在Unix上, “如果args是一个序列,则第一项指定命令字符串,任何其他项都将被视为其他shell参数。 。” 恕我直言,Windows

  • 最近,在使用HttpClient访问Java应用程序中的第三方服务(CURL服务)时,我遇到了如下问题: 我在JDK7中遇到了这个问题。通过对这一问题的研究,我提出了两点建议。 在JDK7TrustStore中添加该特定第三方的证书。[我试过了,但还是遇到了同样的问题] 使用JDK 8而不是JDK 7[我试过了,它对我有效] 单丹