原创

Kotlin使用注解

Kotlin的反射也提供了一些支持注解的API。

一、提取注解信息

Kotlin使用kotlin.Annotion接口来代表程序元素前面的注解,该接口是所有注解的父接口。

Kotlin在kotlin.reflect包下新增了KAnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。该接口主要有如下几个实现类。

  • KCallable:代表可执行的程序实体,如函数和属性。
  • KClass:代表Kotlin的类、接口等类型。
  • KParameter:代表函数和属性的参数。

KAnnotatedElement接口是所有程序元素的父接口,所以程序通过反射获取了某个程序单元对应的KAnnotatedElement对象之后,就可以调用该对象的如下属性和方法来访问注解信息:

  • annotations:List:该属性返回该程序单元的所有注解。
  • <T:Annotation> findAnnotation():T?:根据注解类型返回该程序单元上特定类型的注解。

二、使用注解的示例

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class MyTestable
class MyTest{
    //使用@MyTestable注解执行该方法时需要测试的
    @MyTestable
    fun m1(){}
    
    fun m2(){}

    //使用@MyTestable注解执行该方法时需要测试的
    @MyTestable
    fun m3(){}
    
    fun m4(){}
}

仅仅使用注解来标记程序元素对程序不会有任何影响。

package test0709

import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class MyTestable


class MyTest {
    //使用@MyTestable注解执行该方法时需要测试的
    @MyTestable
    fun m1() {
    }

    fun m2() {}

    //使用@MyTestable注解执行该方法时需要测试的
    @MyTestable
    fun m3() {
    }

    fun m4() {}
}

inline fun <reified T : Any> processTestable() {
    var passed = 0
    var failed = 0
    val target = T::class.createInstance<T>()
    //遍历T对应的类里的所有方法
    for (m in T::class.functions) {
        //如果该方法使用了@Testable修饰
        if (m.findAnnotation<MyTestable>() != null) {
            try {
                //调用m方法
                m.call(target)
                //测试成功,passed计数器加1
                passed++
            } catch (ex: Exception) {
                println("方法${m}运行失败,异常${ex.cause}")
                failed++
            }
        }
    }
    //统计测试结果
    println("共运行了:${passed + failed}个方法,其中:\n失败了:${failed}个,\n成功了:${passed}个!")
}

fun main(args: Array<String>) {
    //处理MyTest类
    processTestable<MyTest>()
}

输出结果:

共运行了:2个方法,其中:
失败了:0个,
成功了:2个!

这是一个标记注解,程序通过判断注解存在与否来决定是否运行指定方法。

package test0709

import java.awt.event.ActionListener
import kotlin.reflect.KClass

@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
//定义一个属性,用于设置元数据
annotation class ActionListenerFor(val listener:KClass<out ActionListener>)
package test0709

import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import javax.swing.*
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties

class AnnotationTest {
    val mainWin = JFrame("使用注解绑定事件监听器")
    //使用注解为OK按键绑定事件监听器
    @ActionListenerFor(listener = OkListener::class)
    val ok = JButton("确定")
    //使用注解为cancel按钮绑定事件监听器
    @ActionListenerFor(listener = CancelListener::class)
    val cancel = JButton("取消")

    fun init() {
        //初始化界面的方法
        val jp = JPanel()
        jp.add(ok)
        jp.add(cancel)
        mainWin.add(jp)
        processAnnotations(this)
        mainWin.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        mainWin.pack()
        mainWin.isVisible = true
    }
}

//定义OK按钮的时间监听器实现类
class OkListener : ActionListener {
    override fun actionPerformed(e: ActionEvent?) {
        JOptionPane.showMessageDialog(null, "单击了确认按钮")
    }
}

//定义cancel按钮的事件监听器实现类
class CancelListener : ActionListener {
    override fun actionPerformed(e: ActionEvent?) {
        JOptionPane.showMessageDialog(null, "单击了取消按钮")
    }
}

//处理注解的方法,其中obj是包含注解的对象
fun processAnnotations(obj: Any) {
    //获取obj对象的类
    val cl = obj::class
    //获取指定obj对象的所有成员,并遍历每个成员
    for (prop in cl.memberProperties) {
        //获取该成员上ActionListenerFor类型的注解
        val a = prop.findAnnotation<ActionListenerFor>()
        //获取属性prop的值
        val fObj = prop.call(obj)
        //如果fObj是AbstractButton的实例,且a不为null
        if (a != null && fObj != null && fObj is AbstractButton) {
            //获取a注解的listener属性值
            val listenerClazz = a.listener
            //使用反射来创建listener类的对象
            val al = listenerClazz.createInstance()
            //为fObj按钮添加事件监听器
            fObj.addActionListener(al)
        }
    }
}

fun main(args: Array<String>) {
    AnnotationTest().init()
}

学海无涯苦作舟

我的微信公众号.jpg

基本语法
  • 作者:HunterArley (联系作者)
  • 发表时间:2019-12-06 09:38
  • 版权声明:本网站部分内容转载于合作站点或其他站点,但都会注明作/译者和原出处。如有不妥之处,敬请指出。
  • 公众号转载:请在文末添加作者公众号二维码