Kotlin | 10. 注解与反射

2021-03-02 15:57:03 浏览数 (1)

本章内容包括:

  • 应用和定义注解
  • 在运行时使用反射对类进行自省
  • 一个真正的 Kotlin 项目实例

10.1 声明并应用注解

代码语言:javascript复制
         /**-------------------------10.1.1 应用注解-------------------------*/

        @Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)"))
        fun remove(index: Int) {
        }

        /*
        * 指定注解实参的语法与Java有些微小的差别:
        *  - 要把一个类指定为注解实参,在类名后加上 ::class:@MyAnnotation (MyClass:class)
        *  - 要把另一个注解指定为一个实参,去掉直接名称前面的 @。
        *  - 要把一个数组指定为一个实参,使用arrayOf函数:@RequestMapping(path = arrayOf("/foo","/bar"))。
        */


//        @Test(timeout = 100L)
//        fun testMethod() {
//        }

        /**-------------------------10.1.2 注解目标-------------------------*/

        /*
        * @get : Rule
        *
        * @get 使用点目标
        * Rule 注解名称
        */

        /*
        class HasTempFolder{
            // 注解的是 getter 而不是属性
            @get :Rule
            val folder = TemporaryFiolder()
        }*/

        /*
        * Kotlin 支持的使用点目标的完整列表如下:
        *  - property  -- Java的注解不能应用这种使用点目标
        *  - field     -- 为属性生成的字段
        *  - get       -- 属性的getter
        *  - set       -- 属性的setter
        *  - receiver  -- 扩展函数护着扩展属性的接收者参数
        *  - param     -- 构造方法的参数
        *  - setparam  -- 属性setter的参数
        *  - delegate  -- 为委托属性存储委托实例的字段
        *  - file      -- 包含在文件中声明的顶层函数和属性的类
        *
        * 用注解控制 JAVA API
        *  - @JvmName       将改变由Kotlin生成的Java方法或字段的名称
        *  - @JvmStatic     能被用在对象声明合作和伴生对象的方法上,把它们暴露成Java的静态方法
        *  - @JvmOverloads  指导Kotlin编译器为带默认参数值的函数生成多个重载(函数)
        *  - @JvmField      可以应用于一个属性,把这个属性暴露成一个没有访问器的共有Java字段
        */


        /**-------------------------10.1.3 使用注解订制 JSON 序列化-------------------------*/
        /*
        *  @JsonExclude 注解用来标记一个属性,这个属性应该排除在序列化和反序列化之外。
        *  @JsonName    注解让你说明代表这个属性的(JSON)键值对之中的键应该是一个给定的字符串,而不是属性的名称。
        */
//        data class Person(@JsonName("alias") val firstName: String, @JsonExclude val age: Int? = null)


        /**-------------------------10.1.4 声明注解-------------------------*/
        // 对拥有参数的注解来说,在类的主构造方法中声明这些参数:
//        annotation class JsonName(val name:String)

        /*Java中声明同样的注解:*/
//        public @ interface JsonName {String value(); }


        /**-------------------------10.1.5 元注解:控制如何处理一个注解-------------------------*/
        // 可以应用到注解类上的注解被称为 元注解。

        // @ Target 元注解说明了注解可以被应用的元素类型。
//        @Target(AnnotationTarget.PROPERTY_CLASS)
//        annotation class JsonExclude

        /*
        *  @Retention 元注解:
        * 说明你声明的注解是否会存储到.class 文件,以及在运行时是否可以通过反射来访问它。
        * Java 默认会在运行时存在,所以Kotlin的默认行为不同:注解拥有RUNTIME保留期。
        * */

        /**-------------------------10.1.6 使用类做注解参数-------------------------*/
        /*
        interface Company {
            val name: String
        }

        data class CompanyImpl(override val name: String) : Company
        data class Person(
                val name: String,
                @DeserializeInterface(CompanyImpl::class) val company: Company
        )

        annotation class DeserializeInterface(val targetClass: KClass<out Any>)
        */

        /**-------------------------10.1.7 使用泛型类做注解参数-------------------------*/

        // @CustomSerializer 注解接收一个自定义序列化器类的引用作为实参。这个序列化器类应该实现 ValueSerializer 接口:
//        interface ValueSerializer<T> {
//            fun toJsonValue(value: T): Any?
//        }

10.2 反射:在运行时对Kotlin对象进行自身

代码语言:javascript复制
        /**-------------------------10.2.1 Kotlin反射API:KClass、KCallable、KFunction、和KProperty-------------------------*/

        // 从Java切换到Kotlin的反射API:
        class Person(val name: String, val age: Int)

        val person = Person("jingbin", 28)
        val kClass = person.javaClass.kotlin// 返回一个 KClass<Person> 的实例
        println(kClass.simpleName)// Person
        kClass.memberProperties.forEach { println(it.name) }// age name

        // 如何通过反射使用call来调用一个函数
//        interface KCallable<out R> {
//            fun call(vararg age: Any?): R
//        }

        fun foo(x: Int) = println(x)
        val kFunction = ::foo
        kFunction.call(42)

        /**-------------------------10.2.2 用反射实现对象序列化-------------------------*/
        // 代码清单10.1 序列化一个对象
//        private fun StringBuilder.serializeObject(obj: Any) {
//            // 取得对象的 XClass
//            val kClass = obj.javaClass.kotlin
//            // 取得类的所有属性
//            val properties = kClass.memberProperties
//
//            properties.joinToStringBuilder(this, prefix = "{", postfix = "}") { prop ->
//                // 取得属性名
//                serializeString(prop.name)
//                append(": ")
//                // 取得属性值
//                serializePropertyValue(prop.get(obj))
//            }
//        }

        /**-------------------------10.2.3 用注解订制序列化-------------------------*/
        // 代码清单10.2 使用属性过滤序列化对象
//        private fun StringBuilder.serializeObject(obj: Any) {
//        obj.javaClass.kotlin.memberProperties
//                .filter { it.findAnnotation<JsonExclude>() == null }
//                .joinToStringBuilder(this, prefix = "{", postfix = "}") {
//                    serializeProperty(it, obj)
//                }
//        }

        // 代码清单10.3 序列化单个属性
//        private fun StringBuilder.serializeProperty(prop: KProperty1<Any, *>, obj: Any) {
//            val jsonNameAnn = prop.findAnnotation<JsonName>()
//            val propName = jsonNameAnn?.name ?: prop.name
//            append(": ")
//            serializePropertyValue(prop.get(obj))
//        }

        // 代码清单10.4 取回属性值的序列化器
        // 代码清单10.5 序列化属性,支持自定义序列化器

        /**-------------------------10.2.4 JSON解析和对象反序列化-------------------------*/
        // 代码清单10.6 JSON解析器回调接口
        // 代码清单10.7 从JSON数据创建对象的接口
        // 代码清单10.8 顶层反序列化函数
        // 代码清单10.9 反序列化一个对象

        /**--------10.2.5 反序列化的最后一步:callBy()和使用反射创建对象-------------------------*/
        // 代码清单10.10 根据值类型取得序列化器
        // 代码清单10.11 Boolean值的序列化器
        // 代码清单10.12 缓存的反射数据的存储
        // 代码清单10.13 构造方法的参数及注解数据的缓存
        // 代码清单10.14 验证需要的参数被提供了

总结

  • Kotlin 中应用注解的语法和 Java 几乎一模一样。
  • 在Kotlin 中可以让你应用注解的目标的范围比 Java 更广,其中包括了文件和表达式。
  • 一个注解的参数可以是一个基本数据类型、一个字符串、一个枚举、一个类引用、一个其他注解类的实例,或者前面这些元素组成的数组。
  • 如果单个 Kotlin 声明产生了多个字节码元素,像@get Rule 这样指定一个注解的使用点目标,允许你选择注解如何应用。
  • 注解类的声明是这样的,它是一个拥有主构造方法且没有类主体的类,其构造方法中所有参数都被标记成 val 属性。
  • 元注解可以用来指定(使用点)目标、保留期模式和其他注解的特性。
  • 反射 API 让你在运行时动态地列举和访问一个对象的方法和属性。它拥有许多接口来表示不同种类的声明,例如类( KClass )、函数( KFunctio川等。
  • 要获取一个 KClass 的实例,如果类是静态己知的,可以使用 ClassName::class :否则,使用 obj.javaClass kotlin 从对象实例上取得类。
  • KFunction 接口和 KProperty 接口都继承了 KCallable ,它提供了 通用的 call 方法。
  • KCallable. callBy 方法能用来调用带默认参数值的方法。
  • KFunctionO、KFunctionl 等这种不同参数数量的函数可以使用 invoke方法调用。
  • KPropertyO 和 KPropertyl 是接收者数量不同的属性,支持用 get 方法取回值。KMutablePropertyO 和 KMutableProperty1 继承了这些接口。支持通过 set 方法来改变属性的值。

0 人点赞