注解(Annotation)
1. 注解基础知识
1.1 什么是注解?
// 注解就是给代码贴标签,提供额外信息@Deprecated("这个方法已经过时了")fun oldMethod() {// 旧代码}// 注解的作用:// 1. 编译时检查:告诉编译器一些信息// 2. 运行时处理:在程序运行时可以读取注解信息// 3. 代码生成:可以自动生成一些代码// 4. 配置信息:替代XML配置文件
1.2 注解的组成部分
// 基本注解声明annotation class MyAnnotation// 带参数的注解声明annotation class MyAnnotation(val value: String)// 多个参数的注解声明annotation class MyAnnotation(val name: String,val age: Int = 0)
1.3 注解的元注解
// @Target - 指定注解可以用在哪里@Target(AnnotationTarget.CLASS) // 只能用在类上annotation class ClassAnnotation@Target(AnnotationTarget.FUNCTION) // 只能用在函数上annotation class FunctionAnnotation@Target(AnnotationTarget.FIELD) // 只能用在字段上annotation class FieldAnnotation// @Retention - 指定注解保留到什么时候@Retention(AnnotationRetention.SOURCE) // 编译时就被丢弃annotation class SourceAnnotation@Retention(AnnotationRetention.BINARY) // 编译后保留,运行时不可用annotation class BinaryAnnotation@Retention(AnnotationRetention.RUNTIME) // 运行时可用(最常用)annotation class RuntimeAnnotation
1.4 注解的Target类型详解
// CLASS - 类、接口、对象@Target(AnnotationTarget.CLASS)annotation class ClassAnnotation// FUNCTION - 函数@Target(AnnotationTarget.FUNCTION)annotation class FunctionAnnotation// FIELD - 字段@Target(AnnotationTarget.FIELD)annotation class FieldAnnotation// PROPERTY - 属性@Target(AnnotationTarget.PROPERTY)annotation class PropertyAnnotation// CONSTRUCTOR - 构造函数@Target(AnnotationTarget.CONSTRUCTOR)annotation class ConstructorAnnotation// VALUE_PARAMETER - 函数参数@Target(AnnotationTarget.VALUE_PARAMETER)annotation class ParameterAnnotation// TYPE - 类型@Target(AnnotationTarget.TYPE)annotation class TypeAnnotation// EXPRESSION - 表达式@Target(AnnotationTarget.EXPRESSION)annotation class ExpressionAnnotation// FILE - 文件@Target(AnnotationTarget.FILE)annotation class FileAnnotation// TYPE_PARAMETER - 类型参数@Target(AnnotationTarget.TYPE_PARAMETER)annotation class TypeParameterAnnotation// PROPERTY_GETTER - 属性getter@Target(AnnotationTarget.PROPERTY_GETTER)annotation class GetterAnnotation// PROPERTY_SETTER - 属性setter@Target(AnnotationTarget.PROPERTY_SETTER)annotation class SetterAnnotation// 多个Target@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)annotation class MultiTargetAnnotation
1.5 注解的Retention类型详解
// SOURCE - 编译时就被丢弃,不会写入class文件@Retention(AnnotationRetention.SOURCE)annotation class SourceAnnotation// 用途:编译时检查,如 @Suppress// BINARY - 编译后保留在class文件中,但运行时不可用@Retention(AnnotationRetention.BINARY)annotation class BinaryAnnotation// 用途:编译时处理,如 @JvmStatic// RUNTIME - 运行时可用,可以通过反射读取@Retention(AnnotationRetention.RUNTIME)annotation class RuntimeAnnotation// 用途:运行时处理,如 @Deprecated
1.6 系统内置注解
// @Override - 重写父类方法override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)}// @Deprecated - 标记已过时@Deprecated("请使用新方法", ReplaceWith("newMethod()"))fun oldMethod() {// 旧代码}// @Suppress - 抑制警告@Suppress("UNUSED_PARAMETER")fun methodWithUnusedParam(param: String) {// 编译器不会警告参数未使用}// @JvmStatic - 生成静态方法@JvmStaticfun staticMethod() {// 这个方法在Java中会被当作静态方法}// @JvmField - 生成字段而不是属性@JvmFieldval constant = "CONSTANT"// @JvmOverloads - 生成重载方法@JvmOverloadsfun methodWithDefaults(param1: String, param2: Int = 0) {// 会生成多个重载方法}
2. 从实际问题出发
2.1 传统方式的问题
// 传统方式:硬编码,模块间直接依赖class LoginActivity {fun onLoginSuccess() {val intent = Intent(this, UserActivity::class.java)startActivity(intent) // 直接依赖具体Activity}}// 问题:// 1. 模块间编译时耦合// 2. 难以进行单元测试// 3. 多应用构建困难// 4. 团队协作冲突
2.2 解决方案:注解 + 处理函数
// 使用注解:声明式编程@Route("/user")class UserActivity : AppCompatActivity() { ... }// 处理函数:自动注册路由fun registerAllRoutes() {val classes = findClassesWithAnnotation(Route::class.java)classes.forEach { clazz ->val annotation = clazz.getAnnotation(Route::class.java)RouterManager.register(annotation.path, clazz as Class<out Activity>)}}
3. 注解的核心概念
3.1 注解的作用机制
// 注解本身不会自动执行任何代码@Importantfun method() { ... } // 仅仅贴上标签,不会自动执行// 需要处理函数来让注解生效fun processAnnotations(obj: Any) {val clazz = obj::class.javafor (method in clazz.declaredMethods) {if (method.isAnnotationPresent(Important::class.java)) {method.invoke(obj) // 手动执行被标记的方法}}}
3.2 系统注解 vs 自定义注解
// 系统注解(内置处理函数)@Override // 编译器自动检查重写@Deprecated // 编译器+IDE自动显示警告@Suppress // 编译器自动抑制警告// 特点:处理函数已经内置在编译器/IDE中// 你感觉不到,但确实有处理函数在工作// 自定义注解(需要自己写处理函数)@Important // 你需要自己写处理函数@Route("/user") // 你需要自己写处理函数@Service("UserService") // 你需要自己写处理函数// 特点:处理函数需要你自己写,自己调用// 这就是注解的"魔法"所在
4. 自定义注解的完整实现
4.1 第一步:定义注解
@Target(AnnotationTarget.FUNCTION) // 只能用在函数上@Retention(AnnotationRetention.RUNTIME) // 运行时可用annotation class Important
4.2 第二步:使用注解
class MyClass {@Importantfun importantMethod1() {println("重要方法1被执行")}fun normalMethod() {println("普通方法被执行")}@Importantfun importantMethod2() {println("重要方法2被执行")}}
4.3 第三步:写处理函数
// 这就是注解的"实现"部分fun callAllImportantMethods(obj: Any) {val clazz = obj::class.javafor (method in clazz.declaredMethods) {if (method.isAnnotationPresent(Important::class.java)) {method.invoke(obj) // 执行被标记的方法}}}
4.4 第四步:调用处理函数
fun main() {val obj = MyClass()callAllImportantMethods(obj) // 手动调用处理函数}// 运行结果:// 重要方法1被执行// 重要方法2被执行
5. 读取注解信息
5.1 运行时读取注解
// 读取类上的注解fun readClassAnnotation() {val clazz = UserActivity::class.java// 检查是否有特定注解if (clazz.isAnnotationPresent(Route::class.java)) {val annotation = clazz.getAnnotation(Route::class.java)println("这个类的路由是: ${annotation.path}")}}// 读取方法上的注解fun readMethodAnnotation() {val clazz = UserService::class.javaval method = clazz.getDeclaredMethod("getUserInfo", String::class.java)// 检查方法上是否有注解if (method.isAnnotationPresent(Test::class.java)) {println("这个方法有测试注解")}}// 读取字段上的注解fun readFieldAnnotation() {val clazz = UserForm::class.javafor (field in clazz.declaredFields) {val annotation = field.getAnnotation(Required::class.java)if (annotation != null) {println("字段 ${field.name} 是必填项")}}}
5.2 注解参数处理
// 带参数的注解@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)annotation class Route(val path: String, val needLogin: Boolean = false)// 使用带参数的注解@Route(path = "/user", needLogin = true)class UserActivity : AppCompatActivity() { ... }// 读取注解参数fun readAnnotationParameters() {val clazz = UserActivity::class.javaval annotation = clazz.getAnnotation(Route::class.java)if (annotation != null) {println("路由路径: ${annotation.path}")println("需要登录: ${annotation.needLogin}")}}
6. 实际项目中的应用案例
6.1 路由注解系统
// 1. 定义路由注解@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)annotation class Route(val path: String)// 2. 使用注解(声明式编程)@Route("/user")class UserActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_user)}}@Route("/login")class LoginActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_login)}}// 3. 处理函数(自动注册)fun registerAllRoutes() {val classes = findClassesWithAnnotation(Route::class.java)classes.forEach { clazz ->val annotation = clazz.getAnnotation(Route::class.java)RouterManager.register(annotation.path, clazz as Class<out Activity>)println("自动注册路由: ${annotation.path} -> ${clazz.simpleName}")}}// 4. 应用启动时调用class MyApplication : Application() {override fun onCreate() {super.onCreate()registerAllRoutes() // 自动注册所有路由}}
6.2 服务注解系统
// 1. 定义服务注解@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)annotation class Service(val name: String = "")// 2. 使用注解@Service("UserService")class UserServiceImpl : UserService {override fun toUserPage(context: Context) {RouterManager.navigate(context, "/user")}}@Service("LoginService")class LoginServiceImpl : LoginService {override fun toLoginPage(context: Context) {RouterManager.navigate(context, "/login")}}// 3. 处理函数(自动注册)fun registerAllServices() {val classes = findClassesWithAnnotation(Service::class.java)classes.forEach { clazz ->val annotation = clazz.getAnnotation(Service::class.java)val instance = clazz.getDeclaredConstructor().newInstance()ServiceManager.register(annotation.name, instance)println("自动注册服务: ${annotation.name}")}}// 4. 应用启动时调用class MyApplication : Application() {override fun onCreate() {super.onCreate()registerAllServices() // 自动注册所有服务}}
6.3 验证注解系统
// 1. 定义验证注解@Target(AnnotationTarget.FIELD)@Retention(AnnotationRetention.RUNTIME)annotation class Required(val message: String = "此字段是必填项")@Target(AnnotationTarget.FIELD)@Retention(AnnotationRetention.RUNTIME)annotation class MinLength(val value: Int, val message: String = "长度不能少于")// 2. 使用注解class UserForm {@Required("用户名是必填项")var username: String = ""@Required("密码是必填项")@MinLength(6, "密码长度不能少于6位")var password: String = ""@MinLength(3, "邮箱长度不能少于3位")var email: String = ""}// 3. 处理函数(自动验证)fun validateForm(form: Any): Boolean {val clazz = form::class.javafor (field in clazz.declaredFields) {field.isAccessible = trueval value = field.get(form) as? String ?: ""// 检查必填项val requiredAnnotation = field.getAnnotation(Required::class.java)if (requiredAnnotation != null && value.isEmpty()) {println("验证失败: ${requiredAnnotation.message}")return false}// 检查最小长度val minLengthAnnotation = field.getAnnotation(MinLength::class.java)if (minLengthAnnotation != null && value.length < minLengthAnnotation.value) {println("验证失败: ${minLengthAnnotation.message} ${minLengthAnnotation.value}")return false}}return true}// 4. 使用验证fun testValidation() {val form = UserForm()form.username = ""form.password = "123"if (validateForm(form)) {println("验证通过")} else {println("验证失败")}}
7. 注解的核心优势
7.1 声明式编程
// 通过注解声明意图,而不是通过代码@Route("/user")class UserActivity : AppCompatActivity() { ... }// 而不是:// RouterManager.register("/user", UserActivity::class.java)
7.2 减少样板代码
// 自动生成重复的代码@AutoRegister@Service("UserService")class UserServiceImpl : UserService { ... }// 而不是手动注册:// ServiceManager.register(UserService::class.java, UserServiceImpl())
7.3 编译时检查
// 在编译时就能发现问题@Requiredvar username: String = "" // 编译时检查必填项
7.4 运行时处理
// 在运行时根据注解信息执行不同逻辑fun processAnnotations(obj: Any) {val clazz = obj::class.javafor (method in clazz.declaredMethods) {if (method.isAnnotationPresent(Important::class.java)) {method.invoke(obj) // 执行重要方法}}}
8. 技术分享的核心要点
8.1 关键理解
- 注解 = 标签:给代码贴标签,提供额外信息
- 处理函数 = 实现:你写的代码,用来处理被注解标记的内容
- 手动调用:注解不会自动生效,需要你手动调用处理函数
8.2 核心原理
- 注解本身不会做任何事情,它只是一个"标签"
- 你需要写处理函数,主动去查找和处理这些标签
- 你需要手动调用处理函数,注解才会起作用
8.3 实际价值
- 模块解耦:通过注解实现模块间的解耦
- 自动化:减少重复代码,提高开发效率
- 类型安全:编译时检查,减少运行时错误
- 可维护性:代码更清晰,更易维护
9. 总结与展望
9.1 核心总结
注解的"魔法" = 注解(标签)+ 处理函数(实现)
- 注解只是"标签",不会自动做任何事情
- 处理函数是你写的代码,用来处理被注解标记的内容
- 你需要手动调用处理函数,注解才会生效
- 注解的优势在于声明式编程、减少样板代码、编译时检查等
9.2 技术展望
- 编译时注解处理:使用APT(Annotation Processing Tool)在编译时生成代码
- 代码生成:自动生成重复的代码,如Builder、Mapper等
- 框架集成:与现有框架(如Dagger、Room、EventBus)结合使用
- 性能优化:通过注解实现性能监控、内存优化等
记住:注解不是自动的,而是你手动实现的!