限定参数范围的注解之 "咖啡店定价" 的故事

让我们用一个 "咖啡店定价" 的故事来理解限定参数范围的注解原理,再结合源码一步步拆解底层逻辑。

故事:咖啡店的 "价格规则标签"

老王开了家咖啡店,规定所有咖啡的价格必须在 10-50 元之间。但店员经常记错价格,导致定价混乱。

于是老王想了个办法:

  1. 设计了一种 "价格范围标签"(类似注解),标签上印着 "最低 10 元,最高 50 元"
  2. 要求店员给所有咖啡的定价单都贴上这个标签(在代码中使用注解)
  3. 专门雇了个 "检查员"(类似注解处理器),每次定价时都对照标签检查价格是否合规

对应到 Java:限定参数范围的注解实现

我们要实现一个@PriceRange注解,用来限制方法参数必须在 10-50 之间。整个过程分 3 步:定义注解使用注解实现检查逻辑

第一步:定义 "价格范围标签"(自定义注解)

就像老王设计标签样式,我们需要定义注解的 "规则":

java 复制代码
import java.lang.annotation.*;

// 这个注解只能贴在方法的参数上
@Target(ElementType.PARAMETER)
// 这个注解在运行时还能被读取到(检查员在定价时还能看到标签)
@Retention(RetentionPolicy.RUNTIME)
public @interface PriceRange {
    // 最低价格(标签上的"最低10元")
    int min() default 10;
    // 最高价格(标签上的"最高50元")
    int max() default 50;
}

这里的@Target@Retention是 "元注解"(注解的注解):

  • @Target(ElementType.PARAMETER):规定这个注解只能贴在方法参数上(就像标签只能贴在定价单上)
  • @Retention(RetentionPolicy.RUNTIME):规定注解在程序运行时仍然有效(检查员在定价时能看到标签,而不是定价单被收起来后就看不到了)

第二步:使用 "价格范围标签"(在代码中应用注解)

就像店员给定价单贴标签,我们在需要限制的参数上加上@PriceRange

java 复制代码
public class CoffeeShop {
    // 给咖啡定价的方法,参数price贴上@PriceRange标签
    public void setCoffeePrice(@PriceRange int price) {
        System.out.println("咖啡定价:" + price + "元");
    }
}

第三步:实现 "检查员"(注解检查逻辑)

老王雇的检查员需要做两件事:

  1. 看到定价单上的标签(读取注解信息)

  2. 检查实际价格是否符合标签上的范围(校验参数值)

在 Java 中,我们可以通过反射实现这个 "检查员":

java 复制代码
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class PriceChecker {
    // 检查方法调用时的参数是否符合@PriceRange规则
    public static void check(Object obj, String methodName, Object... params) throws Exception {
        // 1. 找到要调用的方法(找到定价单)
        Method method = obj.getClass().getMethod(methodName, int.class);
        
        // 2. 获取方法的参数(拿到定价单上的价格输入框)
        Parameter[] parameters = method.getParameters();
        
        // 3. 遍历参数,检查是否有@PriceRange注解
        for (int i = 0; i < parameters.length; i++) {
            Parameter param = parameters[i];
            // 判断参数上是否贴了@PriceRange标签
            if (param.isAnnotationPresent(PriceRange.class)) {
                // 4. 读取注解上的规则(标签上的min和max)
                PriceRange annotation = param.getAnnotation(PriceRange.class);
                int min = annotation.min();
                int max = annotation.max();
                
                // 5. 检查实际参数值是否符合规则(实际价格是否在范围内)
                int price = (int) params[i];
                if (price < min || price > max) {
                    throw new IllegalArgumentException("价格不符合规则!必须在" + min + "-" + max + "元之间");
                }
            }
        }
        
        // 6. 如果检查通过,正常调用方法(定价成功)
        method.invoke(obj, params);
    }

    // 测试一下
    public static void main(String[] args) throws Exception {
        CoffeeShop shop = new CoffeeShop();
        // 正常定价(20元在10-50之间)
        check(shop, "setCoffeePrice", 20); // 输出:咖啡定价:20元
        
        // 错误定价(5元低于最低10元)
        check(shop, "setCoffeePrice", 5); // 抛出异常:价格不符合规则!
    }
}

底层原理总结

限定参数范围的注解本质是 "带规则的标签 + 检查器" 的组合:

  1. 注解定义 :用@interface声明一个 "标签",通过元注解(@Target@Retention)规定标签的使用场景和生命周期,用成员变量(minmax)定义规则。

  2. 注解使用:在需要限制的参数上贴标签,告诉程序 "这个参数需要被检查"。

  3. 检查逻辑:通过反射(运行时)或注解处理器(编译时)实现 "检查员":

    • 反射方式:运行时读取参数上的注解信息,对比参数值和注解规则,不符合就报错。
    • 编译时方式:在编译阶段就检查(类似@Override的原理),直接让代码编译失败。

关键知识点

  • @Retention(RUNTIME)是运行时检查的关键,保证注解在程序运行时还能被读取。

  • 反射是 "检查员" 读取注解信息的工具(getAnnotation()方法)。

  • 注解本身不做任何事情,必须有对应的 "检查器"(代码)才能生效(就像标签本身不会说话,需要检查员来执行规则)。

这样的机制让 Java 代码能在编译或运行时自动校验参数,避免了手动写大量if(price < 10 || price >50)的重复代码。

相关推荐
北京_宏哥1 分钟前
🔥《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
java·前端·面试
鹏多多.42 分钟前
flutter-使用AnimatedDefaultTextStyle实现文本动画
android·前端·css·flutter·ios·html5·web
用户84913717547161 小时前
JDK 17 实战系列(第3期):性能优化与系统增强详解
java·后端·性能优化
Asu52021 小时前
思途spring学习0807
java·开发语言·spring boot·学习
遇见火星1 小时前
Jenkins全链路教程——Jenkins用户权限矩阵配置
java·矩阵·jenkins
埃泽漫笔2 小时前
什么是SpringBoot
java·spring boot
似霰2 小时前
安卓系统属性之androidboot.xxx转换成ro.boot.xxx
android·gitee
zhang1062092 小时前
PDF注释的加载和保存的实现
java·开发语言·pdf·pdfbox·批注
码银2 小时前
什么是逻辑外键?我们要怎么实现逻辑外键?
java·数据库·spring boot
0wioiw02 小时前
Android-Kotlin基础(Jetpack①-ViewModel)
android