首页 > 编程语言 >Java中常见错误-Java中注解是否可以继承

Java中常见错误-Java中注解是否可以继承

时间:2024-06-03 17:31:07浏览次数:26  
标签:INFO main Java 继承 kdz Inherited 注解 Main

Java中注解是否可以继承

在解决这个问题之前需要先了解一下@Inherited

@Inherited

基本概念

@Inherited是Java中的一个元注解,位于java.lang.annotation包内,它用于修饰自定义注解表明这个自定义注解具有继承性。这意味着,如果一个类应用了带有@Inherited的注解,那么它的子类在不显式添加这个注解的情况下,也会被视为拥有这个注解。然而,重要的是要理解**@Inherited的继承特性仅局限于类级别的注解,并不适用于方法、字段、构造器等成员级别的注解**。

使用场景
  • 标记接口实现:如果你定义了一套标记接口,希望实现这些接口的类自动具有某种特征或标记,可以创建一个带有@Inherited的注解来实现这一需求。
  • 框架配置:在构建框架或库时,可能需要定义一些配置注解来指导框架如何处理特定类。使用@Inherited可以让这些配置自动应用到所有子类,减少重复配置
注意事项
  • 不改变运行时行为:@Inherited仅影响编译时反射时对类是否有特定注解的判断,并不直接影响程序的运行时行为
  • 成员注解不适用:@Inherited仅对类级别的注解有效方法、字段等成员的注解即使父类有,子类也不会自动继承
  • 反射获取:即使注解是继承的,使用反射API(如Class.getAnnotation())直接在子类上获取注解时,如果没有在子类上显式声明,可能会得到null。这时可以使用Class.getDeclaredAnnotation()框架提供的工具方法(如Spring的AnnotatedElementUtils.findMergedAnnotation())来查找包括继承在内的注解

由上面可知,有无@Inherited会影响到类级别的注解是否有效,下面按照有无@Inherited注解进行分析

实体类

@MyAnnotation(value = "Class")
@Slf4j
public class Animal {
    @MyAnnotation(value = "Method")
    public void foo() {
    }
    @MyAnnotation(value = "Method")
    public void bar() {
    }
}

@Slf4j
public class Pig extends Animal {
    @Override
    public void foo() {
    }
}

自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited   // 有无,决定着类级别注解可以继承
public @interface MyAnnotation {
    String value();
}

测试方法

@Slf4j
public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        wrong();
        right();
    }

    /**
     * 获取 MyAnnotation 的 value 属性值,如果注解不存在则返回空字符串。
     * @param annotation
     * @return
     */
    private static String getAnnotationValue(MyAnnotation annotation) {
        if (annotation == null) return "";
        return annotation.value();
    }

    public static void wrong() throws NoSuchMethodException {
        Animal animal = new Animal();
        log.info("AnimalClass:{}", getAnnotationValue(animal.getClass().getAnnotation(MyAnnotation.class)));
        log.info("AnimalMethod:{}", getAnnotationValue(animal.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));


        Pig pig = new Pig();
        log.info("PigClass:{}", getAnnotationValue(pig.getClass().getAnnotation(MyAnnotation.class)));
        System.out.println("重写父类的方法");
        log.info("PigMethod1:{}", getAnnotationValue(pig.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));
        System.out.println("继承父类的方法");
        log.info("PigMethod2:{}", getAnnotationValue(pig.getClass().getMethod("bar").getAnnotation(MyAnnotation.class)));


    }

    public static void right() throws NoSuchMethodException {
        Animal animal = new Animal();
        log.info("AnimalClass:{}", getAnnotationValue(animal.getClass().getAnnotation(MyAnnotation.class)));
        log.info("AnimalMethod:{}", getAnnotationValue(animal.getClass().getMethod("foo").getAnnotation(MyAnnotation.class)));

        Pig pig = new Pig();
        log.info("PigClass:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass(), MyAnnotation.class)));
        System.out.println("重写父类的方法");
        log.info("PigMethod1:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass().getMethod("foo"), MyAnnotation.class)));
        System.out.println("继承父类的方法");
        log.info("PigMethod2:{}", getAnnotationValue(AnnotatedElementUtils.findMergedAnnotation(pig.getClass().getMethod("foo"), MyAnnotation.class)));
    }
}

运行结果

使用@Inherited

17:08:16.895 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class //肯定可以看到
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method //肯定可以看到
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class //由于使用了@Inherited注解,可以显示地看到注解
重写父类的方法
17:08:16.897 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1: //子类重写了该方法而没有保留注解,直接通过反射获取时找不到注解
继承父类的方法
17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method //子类继承了该方法,直接通过反射获取时找到注解
17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class


17:08:16.898 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class
重写父类的方法
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1:Method
继承父类的方法
17:08:16.952 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method

在这里插入图片描述

不使用@Inherited

注释掉@Inherited

在这里插入图片描述

17:11:47.786 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class //肯定可以看到
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method //肯定可以看到
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigClass:
重写父类的方法
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1: //由于没有使用@Inherited注解,所以无法看到
继承父类的方法
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method //子类继承了该方法,直接通过反射获取时找到注解

17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalClass:Class
17:11:47.788 [main] INFO com.kdz.annotationinheritance.Main - AnimalMethod:Method
17:11:47.839 [main] INFO com.kdz.annotationinheritance.Main - PigClass:Class
重写父类的方法
17:11:47.840 [main] INFO com.kdz.annotationinheritance.Main - PigMethod1:Method
继承父类的方法
17:11:47.840 [main] INFO com.kdz.annotationinheritance.Main - PigMethod2:Method

在这里插入图片描述

结论

自定义注解上有@Inherited自定义注解上无@Inherited
子类的类上能否继承到父类的类上的注解?×
子类方法,重写了父类上的方法,这个方法能否继承到注解?××
子类方法,继承了父类上的方法,这个方法能否继承到注解?

使用特定工具方法(如Spring的AnnotatedElementUtils.findMergedAnnotation())来合并注解信息,父类的类级别、方法注解对子类都是可见

标签:INFO,main,Java,继承,kdz,Inherited,注解,Main
From: https://blog.csdn.net/kdzandlbj/article/details/139420294

相关文章

  • javaScript基础22
    一、概念一门客户端脚本语言运行在客户端浏览器中的。每一个浏览器都有JavaScript的解析引擎脚本语言:不需要编译,直接就可以被浏览器解析执行了二、功能可以来增强用户和html页面的交互过程,可以来控制html元素,让页面有一些动态的效果,增强用户的体验。三、JavaScript发......
  • Java多线程
    线程的定义Java线程是Java编程语言中的执行单元。在Java中,线程可以看作是轻量级的进程,它独立运行,具有自己的执行路径。线程的原理Java线程的实现基于操作系统的线程模型,但Java虚拟机(JVM)对线程的管理和调度做了封装和优化,使得Java线程更加可控和可靠。下面是Java线程的一些基本......
  • 2.3Docker部署java工程
    2.3Docker部署java工程1.导入jar包2.在Docker部署jdk(容器名为myjdk17)3.修改jar包名mv原包名新包名4.配置启动脚本Dockerfile是一个文本文件,其中包含了构建Docker镜像所需的一系列步骤和指令。通过编写Dockerfile文件,可以生成我们想要的镜像。基于JDK17镜像使......
  • Java18新特性有哪些
    Java18于2022年3月22日正式发布,它带来了一些新特性和改进,主要包括以下几点:JEP400:UTF-8byDefault123:JDK将UTF-8设置为默认字符集,这使得依赖于默认字符集的API在所有实现、操作系统、区域设置和配置中保持一致。JEP408:SimpleWebServer123:引入了一个简......
  • TypeScript与JavaScript之间的关系
    TypeScript是一种JavaScript的超集语言,这意味着任何有效的JavaScript代码在TypeScript中都是合法的。TypeScript的设计并不意图打破或改变JavaScript的语法,因此您可以将现有的JavaScript代码直接放入TypeScript文件中,无需担心代码的结构或格式。然而,TypeScript不仅仅是JavaScrip......
  • JavaEE初阶--锁进阶理解
    目录一、引言二、锁的分类1.乐观锁vs悲观锁2.重量级锁vs轻量级锁3.自旋锁vs挂起等待锁4.公平锁vs非公平锁5.可重入锁vs不可重入锁6.读写锁三、CAS1.什么是CAS?2.CAS伪代码3.CAS的实现4.CAS的应用5.CAS的ABA问题四、总结一、引言 前面的博客我们......
  • Java邮件发送的基本流程是什么?如何实现?
    Java邮件发送的SMTP服务器如何配置?怎么配置发信?Java邮件发送是在Java应用程序中通过邮件协议(如SMTP)发送电子邮件的过程。它是许多企业级应用程序中常见的功能,用于向用户发送通知、确认邮件等。AokSend将介绍Java邮件发送的基本流程,以及其中涉及的关键步骤。Java邮件发送:创建......
  • 【Java数据结构】详解Stack与Queue(一)
    ......
  • java冒泡法代码
    importjava.util.Arrays;publicclasshhh{publicstaticint[]pao(int[]arr){for(inti=0;i<arr.length-1;i++){for(intj=0;j<arr.length-1-i;j++){if(arr[j]>arr[j+1]){......
  • JavaScript第三讲:解锁JavaScript的数据世界:基本数据类型与类型转换的奥秘
    前言:hello,大家好,在JavaScript的编程世界中,数据是构成一切的基础。无论是构建复杂的Web应用,还是处理用户输入,我们都无法绕开数据这一核心元素。而在JavaScript中,数据以不同的形式存在,我们称之为数据类型。理解这些数据类型以及它们之间的转换关系,对于编写高效、健壮的代码至关......