内部类组织语法树解析逻辑的核心是按职责分层、隔离变化、复用上下文;局部内部类用于单次定制扫描并捕获外部变量,静态内部类封装可复用ast工具逻辑。

用局部内部类封装单次遍历任务
当需要对某个类声明做一次定制化扫描(比如只提取带 `@BindView` 的字段并校验命名规范),适合在 `visitClass` 或 `process` 方法体内定义局部内部类。它能直接捕获外部方法的临时变量(如 `TypeElement enclosingClass`、`Set例如:
在 ButterKnifeProcessor 的 RScanner 就是局部内部类,但它被提升为成员内部类——因为需多次复用;而若某次解析仅用于生成一个辅助校验报告,用局部类更轻量。
好处包括:
- 作用域封闭:不会污染外层类的命名空间
- 天然访问外部方法变量和 final 参数
- 逻辑聚拢:解析、收集、报错全在一个小类里,可读性强
用静态内部类封装可复用的 AST 工具逻辑
像跳过泛型擦除后的类型比较、提取方法签名哈希、判断是否为 Android View 子类等通用能力,不适合耦合在处理器主流程中。用 static 内部类封装,既不依赖外部实例,又能放在同一源文件内保持内聚。例如:
定义 static class AstUtils,提供:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
-
isViewSubtype(TypeMirror type)—— 基于Types.isAssignable()判断 -
getSimpleMethodName(JCMethodDecl method)—— 安全提取无泛型的方法名 -
findAnnotationOn(JCTree node, Class extends Annotation> ann)—— 递归查找节点及父节点上的注解
这类工具类不持有状态,无副作用,可被多个访问者共用,也方便单元测试。
用成员内部类实现访问者模式的子策略
`AbstractProcessor` 本身不强制访问者模式,但处理复杂 AST 时,继承 `TreePathScanner比如:
-
private class ViewBindingScanner extends TreePathScanner<void void></void>—— 只处理@BindView和视图字段 -
private class EventBindingScanner extends TreePathScanner<void void></void>—— 专扫@OnClick方法签名合法性 -
private class ResourceBindingValidator extends TreePathScanner<void void></void>—— 检查@BindString引用的资源 ID 是否真实存在
它们共享外部类持有的 Elements、Types、Messager 等工具,又各自独立维护扫描状态(如当前类名、是否在匿名类内),避免大而全的单个访问者越来越难维护。
避免滥用:什么情况不该用内部类?
内部类不是银弹。以下场景建议用独立类或函数式接口:
- 逻辑需跨多个注解处理器复用 → 提取为模块化工具包(如
ast-core) - 要序列化或跨进程传递 → 内部类含隐式引用,序列化会失败
- 单个方法已足够清晰(如只遍历字段并收集名称)→ 直接用 Lambda 或私有方法更直观
- 内部类超过 150 行或含 3 个以上嵌套层级 → 说明职责未切分,该重构了
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










