让我们用一个 "咖啡店定价" 的故事来理解限定参数范围的注解原理,再结合源码一步步拆解底层逻辑。
故事:咖啡店的 "价格规则标签"
老王开了家咖啡店,规定所有咖啡的价格必须在 10-50 元之间。但店员经常记错价格,导致定价混乱。
于是老王想了个办法:
- 设计了一种 "价格范围标签"(类似注解),标签上印着 "最低 10 元,最高 50 元"
- 要求店员给所有咖啡的定价单都贴上这个标签(在代码中使用注解)
- 专门雇了个 "检查员"(类似注解处理器),每次定价时都对照标签检查价格是否合规
对应到 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 + "元");
}
}
第三步:实现 "检查员"(注解检查逻辑)
老王雇的检查员需要做两件事:
-
看到定价单上的标签(读取注解信息)
-
检查实际价格是否符合标签上的范围(校验参数值)
在 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); // 抛出异常:价格不符合规则!
}
}
底层原理总结
限定参数范围的注解本质是 "带规则的标签 + 检查器" 的组合:
-
注解定义 :用
@interface
声明一个 "标签",通过元注解(@Target
、@Retention
)规定标签的使用场景和生命周期,用成员变量(min
、max
)定义规则。 -
注解使用:在需要限制的参数上贴标签,告诉程序 "这个参数需要被检查"。
-
检查逻辑:通过反射(运行时)或注解处理器(编译时)实现 "检查员":
- 反射方式:运行时读取参数上的注解信息,对比参数值和注解规则,不符合就报错。
- 编译时方式:在编译阶段就检查(类似
@Override
的原理),直接让代码编译失败。
关键知识点
-
@Retention(RUNTIME)
是运行时检查的关键,保证注解在程序运行时还能被读取。 -
反射是 "检查员" 读取注解信息的工具(
getAnnotation()
方法)。 -
注解本身不做任何事情,必须有对应的 "检查器"(代码)才能生效(就像标签本身不会说话,需要检查员来执行规则)。
这样的机制让 Java 代码能在编译或运行时自动校验参数,避免了手动写大量if(price < 10 || price >50)
的重复代码。