暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

踩坑日记 | 可重复注解

法欧特的杂谈小铺 2021-05-24
731

今天又是一个晴朗的午后,我也像往常一样,忙里偷闲的维护着我的 simple-robot (下文简称为simbot) 框架,写着一些没什么技术含量的垃圾代码。


如果你对simple-robot这个框架有兴趣,可以来看看~ https://github.com/ForteScarlet/simpler-robot



众所周知,simbot 1.x 版本时代的时候是以Java为主要开发语言,而simbot 2.x版本之后,绝大部分代码是由kotlin开发的。尽管kotlin也是可以作为JVM平台语言开发的,但是在从Java转到kotlin的时候也是必不可免的会踩坑的。


而这次,这个坑就踩到了注解中。


在simbot1.x中,有一个注解叫做@Listen:

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.TYPE, ElementType.METHOD}) 
public @interface Listen {
/**
     * 监听器所监听的类型, 可以监听多个类型。
*/
    Class<? extends MsgGet>[] value();


    /** 一些其他的参数.... */
}


@Listen用于标注在一个方法上,用来标识这个方法可以监听一些指定的事件类型。


从上面的代码可以看出来,@Listen注解有一个 value 参数,这个value是一个类型( Class<? extends MsgGet> )数组,它代表  @Listen 可以监听一个或者多个类型的事件(MsgGet)。


考虑到可以监听多个事件,但是却只有一个注解,似乎不太便于解析与理解,因此在2.x版本中,我将一个 @Listen 变更为了一个 @Listens 或者多个 @Listen,也就是使用了Java之后的重复注解特性:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Documented
@Repeatable(Listens.class)
public @interface Listen {
/**
* 具体的监听类型。
*/
    Class<? extends MsgGetvalue();
}


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Documented
public @interface Listens {


/**
     * 监听注解 @Listen 的数组。
*/
Listen[] value();


    /* 一些其他参数... */




}



是时候复习一下Java的可重复注解特性了,首先,一个可重复注解(或者说一套可重复注解)中,会存在两种注解:父注解和子注解。其中,子注解是你真正进行重复的注解,就好比上面提到的 @Listen 注解,它在使用的时候是这个样子的:

@Listen(PrivateMsg.class)
@Listen(GroupMsg.class)
public void testFunction() {
// ....
}


可以看到,上面的 testFunction 方法标注了两个 @Listen 注解,这时候实际上在编译后的效果就类似于:

@Listens(value = {
@Listen(PrivateMsg.class),
@Listen(GroupMsg.class)
})
public void testFunction() {
// ....
}


可以看到,这两个 @Listen 被收到了 @Listens 注解的 value 参数中了。


这就是java的可重复注解的基本用法。但是当我带着这种思维走进了kotlin的代码丛林的时候,却发现有些东西却不尽人意。


实际上,kotlin也是有Repeatable元注解的。

参考:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.annotation/-repeatable/


当看到这个的时候,我很是开心,心想着可以直接去用kotlin的注解就好了。但是事后我才发现实际上并没有那么称心如意。

我首先写了一个注解:

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
annotation class Listen(val value: KClass<out MsgGet>)


直到目前为止还没有什么问题,但是这时候已经暴露出一些问题了。

@Repeatable并没有指定一个父容器,那么它如何表示多个注解呢?


果不其然,在我进行测试的时候出现了问题:

@Listen(MsgGet::class)
@Listen(MsgGet::class)
fun testFunc() = 1


Repeatable annotations with non-SOURCE retention are not yet supported


很明显的提示,它说目前 @Repeatable 注解只支持在源码级别存在,而不支持在运行时进行使用。


而其实有关这方面的支持,在kotlin官方的youtrack中也是有的:KT-12794

参考:https://youtrack.jetbrains.com/issue/KT-12794


但是具体为什么kotlin会遗漏这方面的功能——或者说尚未设计这方面的功能,却不太好说。有可能是因为最开始kotlin默认的编译版本是1.6(现在1.6目标已经被弃用,而默认版本变更为了1.8),且同时可重复注解真的很少见而导致官方对这方面的支持出现的遗漏。

当然,说不定也会有一些更深层次的含义,比如涉及到MMP之间的兼容性的问题等等。。。



但是不论原因如何,但现在这个问题确确实实的存在是不争的事实。

因此现在也就只能暂时使用java定义可重复注解了。





实际上,这篇文档标题写着“今天”,但是这个草稿已经被搁置了半年之久





文章转载自法欧特的杂谈小铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论