入行没几年的小码农,近期学习Kotlin,做一份笔记记录,此文依据《Kotlin实战》这本书的流程记录,部分示例内容均摘自《Kotlin实战》,记下自己的理解,本篇将记录注解和反射的知识点。
Kotlin学习笔记系列
新手上路,Kotlin学习笔记(一)-- Kotlin入门介绍
新手上路,Kotlin学习笔记(二)---方法(函数)部分
新手上路,Kotlin学习笔记(三)---类、对象、接口
新手上路,Kotlin学习笔记(四)---Lambda表达式在Kotlin中的使用
新手上路,Kotlin学习笔记(五)---Kotlin中的类型系统
新手上路,Kotlin学习笔记(六)---运算符重载和其它约定
新手上路,Kotlin学习笔记(七)---Lambda作为形参和返回值的使用
新手上路,Kotlin学习笔记(八)---泛型的使用
新手上路,Kotlin学习笔记(九)---注解和反射
一、注解
1、注解的使用方式
和java中一样,注解使用的时候使用@符号作为前缀,但是在注解实参的使用上,和Java有一些不同之处
(1)把一个类指定为注解实参的时候,需要加上::class ,如 @MyAnnotation(MyClass::class)
(2)将另一个注解指定为注解实参的时候,需要去掉注解前的@符号,如下面ReplaceWith是另一个注解
在Kotlin中如果使用Deprecated注解,还可以在其实参中使用ReplaceWith注解,这样使用后,在调用过期的方法时,将会有使用新方法的快速修正功能。
(3)把一个数组指定为一个实参的时候,使用arrayOf函数,如@RequestMapping(path= arrayOf("/foo", "/bar"))。如果注解类是在Java中声明的,命名为value的形参按需自动地被转换成可变长度的形参,所以不用arrayOf函数就可以提供多个实参。
注解的实参需要在编译期就是已知的,所以如果要把一个属性当做注解使用,必须用const编辑,并且必须是基本数据类型或者String类型的值。
2、注解的对象
在Kotlin中,我们前面了解到,一个属性可能对应多个Java声明,如一个属性就对应一个Java字段、get方法、set方法和其参数。所以我们在指定注解对象的时候,就需要使用点目标来确定要对哪个对象注解。
使用点目标声明要被注解的对象,将要注解的内容放在@符号和注解名之间,并用冒号 : 分隔,如@get:Rule就是对get方法使用Rule注解
如果使用Java中的声明的注解来注解一个属性,会默认的应用到相应的字段上,Kotlin中也可以指定对应属性的注解,其对应关系如下:
property Java的注解不能应用这种使用点目标
field 为属性生成的字段
get 属性的getter
set 属性的setter
receiver 扩展函数或者扩展属性的接收者参数
param 构造方法的参数
setparam 属性setter的参数
delegate 为委托属性存储委托实例的字段
file 包含在文件中声名的顶层函数和属性的类(任何应用到file目标的注解都必须放在文件的顶层,放在package指令之前)
tips:Kotlin允许对任意的表达式声明注解,而不仅仅是类和函数的声明及类型。
3、声明注解
现在我们看如何声明一个注解,代码如下
@Target(allowedTargets = AnnotationTarget.FIELD)
annotation class JsonName(val name: String)//如果没有参数,可以省略括号()
很简单,只需要加上annotation关键字即可,如果有参数,就在后面括号中进行声明。元注解在该注解上方使用即可。
二、反射
在Java中我们就知道,注解的使用一定跟随着反射,没有反射的注解没有什么实际意义。接下来我们看反射是如何在Kotlin中使用的。
1、Kotlin中的反射API
在kotlin的反射中,我们会接触到两种API,java原生的API和Kotlin特有的反射API,它可以解决一些在Java中不存在的概念,如可空类型。
Kotlin反射API的主要入口是KClass类,它代表一个类,可以获取到该类和其超类中包含的所有声明。MyClass::class这样的写法可以获取到一个KClass实例,如果需要在运行过程中获取一个对象的类,可以先使用javaClass属性获取Java的Class对象,然后访问.kotlin扩展属性获取KClass对象。如 person.javaClass.kotlin
接下来我们看一下部分源码中的KClass
/**
* Represents a class and provides introspection capabilities.
* Instances of this class are obtainable by the `::class` syntax.
* See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/reflection.html#class-references)
* for more information.
*
* @param T the type of the class.
*/
public interface KClass : KDeclarationContainer, KAnnotatedElement, KClassifier {
/**
* The simple name of the class as it was declared in the source code,
* or `null` if the class has no name (if, for example, it is an anonymous object literal).
*/
public val simpleName: String?
/**
* The fully qualified dot-separated name of the class,
* or `null` if the class is local or it is an anonymous object literal.
*/
public val qualifiedName: String?
/**
* All functions and properties accessible in this class, including those declared in this class
* and all of its superclasses. Does not include constructors.
*/
override val members: Collection>
/**
* All constructors declared in this class.
*/
public val constructors: Collection>
}
一个类中所有的成员和函数都放在members对象中,他是一个KCallable接口组成的集合,那么我们看下KCallable接口的代码是什么样的。
/**
* Represents a callable entity, such as a function or a property.
*
* @param R return type of the callable.
*/
public interface KCallable : KAnnotatedElement {
/**
* The name of this callable as it was declared in the source code.
* If the callable has no name, a special invented name is created.
* Nameless callables include:
* - constructors have the name "",
* - property accessors: the getter for a property named "foo" will have the name "",
* the setter, similarly, will have the name "".
*/
public val name: String
/**
* Parameters required to make a call to this callable.
* If this callable requires a `this` instance or an extension receiver parameter,
* they come first in the list in that order.
*/
public val parameters: List
/**
* The type of values returned by this callable.
*/
public val returnType: KType
/**
* The list of type parameters of this callable.
*/
@SinceKotlin("1.1")
public val typeParameters: List
/**
* Calls this callable with the specified list of arguments and returns the result.
* Throws an exception if the number of specified arguments is not equal to the size of [parameters],
* or if their types do not match the types of the parameters.
*/
public fun call(vararg args: Any?): R
/**
* Calls this callable with the specified mapping of parameters to arguments and returns the result.
* If a parameter is not found in the mapping and is not optional (as per [KParameter.isOptional]),
* or its type does not match the type of the provided value, an exception is thrown.
*/
public fun callBy(args: Map): R
/**
* Visibility of this callable, or `null` if its visibility cannot be represented in Kotlin.
*/
@SinceKotlin("1.1")
public val visibility: KVisibility?
/**
* `true` if this callable is `final`.
*/
@SinceKotlin("1.1")
public val isFinal: Boolean
/**
* `true` if this callable is `open`.
*/
@SinceKotlin("1.1")
public val isOpen: Boolean
/**
* `true` if this callable is `abstract`.
*/
@SinceKotlin("1.1")
public val isAbstract: Boolean
}
这里面的call就是我们调用这个函数或者这个属性get方法的地方。
接下来我们看KClass中的constructors,它是一个KFunction的实例组成的集合,这又是什么呢?
/**
* Represents a function with introspection capabilities.
*/
public interface KFunction : KCallable, Function {
/**
* `true` if this function is `inline`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/inline-functions.html)
* for more information.
*/
@SinceKotlin("1.1")
public val isInline: Boolean
/**
* `true` if this function is `external`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/java-interop.html#using-jni-with-kotlin)
* for more information.
*/
@SinceKotlin("1.1")
public val isExternal: Boolean
/**
* `true` if this function is `operator`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/operator-overloading.html)
* for more information.
*/
@SinceKotlin("1.1")
public val isOperator: Boolean
/**
* `true` if this function is `infix`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/functions.html#infix-notation)
* for more information.
*/
@SinceKotlin("1.1")
public val isInfix: Boolean
/**
* `true` if this is a suspending function.
*/
@SinceKotlin("1.1")
public val isSuspend: Boolean
}
可以看到他实现了KCallable接口,实际上我们在前面学习Lambda的时候,使用的成员引用的形式获取一个方法的引用,如person::showAddr,就是获取到的一个KFunction对象,KFunction同时也实现了Function接口,该接口将会对应FunctionN的接口(这里是自动处理的,源码中暂时未找到相关说明),FunctionN接口会对应指定的参数和返回值,刚好供一个方法使用。如Function2的声明如下,表示两个参数P1,P2和一个返回值R,刚好对应两个参数一个返回值的方法。
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package kotlin.jvm.functions
public interface Function2 : kotlin.Function {
public abstract operator fun invoke(p1: P1, p2: P2): R
}
接着我们就能使用invoke来调用指定的方法,这样做的好处在于不会使用到不存在的方法,使用call的时候如果参数数量不匹配是会抛出异常的。使用示例如下:
fun sum(a: Int, b: Int) = a + b
fun reflect()
{
val sumMethod = this::sum
sumMethod.invoke(1, 2)//使用call的话,如果参数不匹配,将会在运行时抛出异常,而invoke会强制要求参数匹配
}
最后我们来看另一个类KProperty,这个类是我们获取属性的时候,实际取到的类型。
/**
* Represents a property, such as a named `val` or `var` declaration.
* Instances of this class are obtainable by the `::` operator.
* See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/reflection.html)
* for more information.
*
* @param R the type of the property.
*/
public interface KProperty : KCallable {
/**
* `true` if this property is `lateinit`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties)
* for more information.
*/
@SinceKotlin("1.1")
public val isLateinit: Boolean
/**
* `true` if this property is `const`.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/properties.html#compile-time-constants)
* for more information.
*/
@SinceKotlin("1.1")
public val isConst: Boolean
/** The getter of this property, used to obtain the value of the property. */
public val getter: Getter
/**
* Represents a property accessor, which is a `get` or `set` method declared alongside the property.
* See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/properties.html#getters-and-setters)
* for more information.
*
* @param R the type of the property, which it is an accessor of.
*/
public interface Accessor {
/** The property which this accessor is originated from. */
public val property: KProperty
}
/**
* Getter of the property is a `get` method declared alongside the property.
*/
public interface Getter : Accessor, KFunction
}
/**
* Represents a property declared as a `var`.
*/
public interface KMutableProperty : KProperty {
/** The setter of this mutable property, used to change the value of the property. */
public val setter: Setter
/**
* Setter of the property is a `set` method declared alongside the property.
*/
public interface Setter : KProperty.Accessor, KFunction
}
它也实现了KCallable接口,不过其实现KProperty0和KProperty1,KProperty2提供了一个get的方法,该方法用于获取该属性的值。KProperty0的get方法没有参数,用于直接获取顶层属性的值。KProperty1的get方法有一个参数,用于获取该属性在对应对象上的值。同理KMutableProperty就是对应的set功能。简单使用示例如下:
var count = 0//此处是一个顶层属性
fun reflect()
{
val sumMethod = this::sum
sumMethod.invoke(1, 2)
val person: Person = Person(age = 18, name = "Bob", addr = "address")
val kProperty0 = ::count
kProperty0.get()//此处获count的值
kProperty0.set(100)//此处设置count的值
val kProperty1 = Person :: addr
kProperty1.get(person) //此处获取person对象addr的值
kProperty1.set(person,"new address") //此处设置person对象新的addr的值
}
Kotlin的学习笔记到此为止就算结束了,感谢大家的阅读,有错误及不妥之处欢迎留言指正,让我们一起共同学习进步。
欢迎大家关注我的其他文章,后面会有Kotlin相关的实际使用等文章,也会有Android相关的其它内容。