场景还原:为什么需要反射?
想象这样一个场景:我们正在开发一个数据可视化系统。系统需要根据用户的不同查询需求,动态地从图表参数对象中提取特定属性。
传统方法的局限性
传统的硬编码方式会导致:
代码重复
扩展性差
每增加一个属性就要修改代码
问题的具体场景
java
// 图表参数对象
public class ChartsParam {
private List<String> categories; // 类别列表
private List<Integer> values; // 数值列表
private String title; // 图表标题
private boolean isMultiSeries; // 是否多序列
}
// 统计过滤器
public class StatisticsFilter {
private String property; // 要查找的属性名
}
我们的目标是:根据 StatisticsFilter 中指定的 property,从 ChartsParam 中动态获取对应的属性值。下面是反射的具体实现:
java
public List<StatisticsResult> processChartParameters(
List<StatisticsFilter> panelParams,
ChartsParam chartsParam
) {
List<StatisticsResult> results = new ArrayList<>();
for (StatisticsFilter panelParam : panelParams) {
String property = panelParam.getProperty();
try {
// 关键步骤1:获取字段
Field field = chartsParam.getClass().getDeclaredField(property);
// 关键步骤2:设置可访问
field.setAccessible(true);
// 关键步骤3:获取字段值
Object value = field.get(chartsParam);
// 关键步骤4:根据不同类型处理
if (value != null) {
StatisticsResult result = new StatisticsResult();
result.setProperty(property);
result.setValue(value);
results.add(result);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
// 优雅地处理异常
log.warn("属性 {} 不存在或无法访问", property, e);
}
}
return results;
}
深入解析反射的每个步骤
- 获取字段 getDeclaredField()
作用:根据属性名动态获取对应的 Field 对象
特点:可以获取私有字段,不受访问修饰符限制 - 设置可访问 setAccessible(true)
作用:突破 Java 的访问权限控制
意义:允许访问私有、受保护的字段 - 获取字段值 field.get()
作用:从对象中获取指定字段的实际值
灵活性:可以获取任意类型的字段值
使用示例:
java
public void demonstrateReflectionUsage() {
// 构造图表参数
ChartsParam chartsParam = new ChartsParam();
chartsParam.setCategories(Arrays.asList("北京", "上海", "广州"));
chartsParam.setValues(Arrays.asList(100, 200, 150));
chartsParam.setTitle("城市销售对比");
chartsParam.setMultiSeries(true);
// 定义要获取的属性
List<StatisticsFilter> panelParams = Arrays.asList(
new StatisticsFilter("categories"),
new StatisticsFilter("values")
);
// 调用反射处理方法
List<StatisticsResult> results = processChartParameters(panelParams, chartsParam);
// 输出结果
results.forEach(result ->
System.out.println(result.getProperty() + ": " + result.getValue())
);
}
优点
动态性:运行时决定要处理的属性
灵活性:可以处理未知的属性
扩展性:添加新属性无需修改核心代码
局限性
性能开销较大
失去编译期类型检查
可能破坏封装性