499. Java 反射 - 获取类型上的注解

499. Java 反射 - 获取类型上的注解

1. 背景介绍

  • Java 8 引入了 TYPE_USE 注解目标 ,意味着注解不仅可以标记在类、方法、字段上,还可以直接标记在类型使用的位置。
  • 举例:继承、实现接口、方法参数、返回值、异常声明等地方都可以放置注注解。
  • 为了支持这种能力,反射 API 提供了 AnnotatedType 接口,用来访问类型上的注解。

2. 定义一个 @NonNull 注解

java 复制代码
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface NonNull {}

这里用 TYPE_USE 表示:注解可以用在"类型使用"的地方,而不是仅仅在"类型定义"上。


3. 在类型使用上应用注解

java 复制代码
public class Person {}

public class User
        extends @NonNull Person
        implements @NonNull Serializable {}

👉 这里:

  • User 继承了 @NonNull Person
  • User 实现了 @NonNull Serializable

4. 使用反射获取类型注解

java 复制代码
Class<?> c = User.class;

// 获取父类上的注解
AnnotatedType superClass = c.getAnnotatedSuperclass();
for (Annotation annotation : superClass.getAnnotations()) {
    System.out.println("annotation on the super class = " + annotation);
}

// 获取接口上的注解
AnnotatedType[] interfaces = c.getAnnotatedInterfaces();
for (AnnotatedType annotatedInterface : interfaces) {
    for (Annotation annotation : annotatedInterface.getAnnotations()) {
        System.out.println("annotation on the implemented interface = " + annotation);
    }
}

运行结果:

java 复制代码
annotation on the super class = @org.devjava.NonNull()
annotation on the implemented interface = @org.devjava.NonNull()

💡 要点:

  • getAnnotatedSuperclass():查看父类上的类型注解。
  • getAnnotatedInterfaces():查看接口上的类型注解。

5. 在异常声明上使用注解

有时我们希望给异常类型本身加注解,比如告诉调用者某个异常可以安全地转化为 RuntimeException。

定义注解

java 复制代码
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReThrowAsRuntimeException {}

在构造器上使用

java 复制代码
class EmptyStringException extends Exception {
    public EmptyStringException(String message) {
        super(message);
    }
}

public class Message {
    private final String name;

    public Message(String name) throws @ReThrowAsRuntimeException EmptyStringException {
        if (name == null || name.isEmpty()) {
            throw new EmptyStringException("name is empty");
        }
        this.name = name;
    }
}

6. 客户端代码示例

java 复制代码
try {
    Message message = new Message(""); // 会触发异常
} catch (EmptyStringException e) {
    Constructor<?> constructor = Message.class.getConstructor(String.class);

    AnnotatedType[] exceptionTypes = constructor.getAnnotatedExceptionTypes();
    for (AnnotatedType exceptionType : exceptionTypes) {
        if (exceptionType.isAnnotationPresent(ReThrowAsRuntimeException.class)) {
            throw new RuntimeException(e); // 自动包装成运行时异常
        }
    }
}

运行结果:

java 复制代码
Exception in thread "main" java.lang.RuntimeException: org.devjava.EmptyStringException: name is empty
Caused by: org.devjava.EmptyStringException: name is empty

7. 总结

  • 普通注解 用在类、方法、字段;类型注解用在"类型使用"的地方。
  • 使用反射时:
    • getAnnotatedSuperclass() → 父类的类型注解
    • getAnnotatedInterfaces() → 接口的类型注解
    • getAnnotatedExceptionTypes() → 异常类型的注解
  • 这种能力非常适合做 代码质量检查、契约验证 (比如 @NonNull)、以及 异常处理策略 (比如 @ReThrowAsRuntimeException)。
相关推荐
西安小哥5 分钟前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
学长毕设11 分钟前
基于SpringBoot的游乐园管理系统-附源码
java·spring boot·后端
橙序员小站22 分钟前
Windows 安装包反复报错?一次排查补上了 5 个 CI 检查点
后端
陈随易27 分钟前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
SimonKing38 分钟前
震惊:怎么回事,opencode一轮对话耗尽免费额度
java·后端·程序员
恋猫de小郭1 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
张彦峰ZYF1 小时前
功能齐了不等于能上线:智能工单系统的AI回归测试、安全防护、成本可观测与CI/CD生产部署
后端·企业智能工单系统·评测线·安全线·成本线·部署线
Rain的Java大神之路1 小时前
LeetCode 37 解数独面试攻略:回溯 + 位掩码 + MRV,从暴力到毫秒级求解
java·后端·leetcode
量化分析码农1 小时前
【Python量化波动率实战 05】你的组合真的分散了吗:滚动相关与分散化收益
后端
严肃小白兔2 小时前
TanStack Router 更新构建工具链:Node.js 兼容性、路由分割与前端工程实践
前端·node.js·状态模式