前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何让注解处理器支持 Kotlin?

如何让注解处理器支持 Kotlin?

作者头像
bennyhuo
发布2020-02-20 13:19:10
2.3K0
发布2020-02-20 13:19:10
举报
文章被收录于专栏:BennyhuoBennyhuo

友情提醒:如果没搞过注解处理器,这篇文章你看起来可能会比较迷。。

什么是注解处理器

话说,最近尝试了一下写了个注解处理器,也就是我们常见的 apt,在 Kotlin 当中有个插件叫 kapt,说的就是注解处理器。注解处理器能干什么呢?能帮我们生成一些代码,让我们变懒,让我们的代码变优雅(也许吧)。

需要注意的是,这个注解处理器是 Java 编译器的特性,而 Java 编译器根本不知道 Kotlin 是神马东西,于是乎,如果大家在 Android 当中用到了 kapt 这个插件,你就会发现在 build/tmp/kapt3 下面有个 stubs 目录:

这个目录里面会有从你的 Kotlin 源码生成的 Java 源码,注解处理器后面会跟据这些源码去做注解处理,这实际上就是 kapt 的原理啦,如果你之前看到过官方写的介绍 kapt 原理的文章,里面说的 stubs ,就是这个。

话说到这儿,不得不提一句,既然注解处理器是 Java 编译器的特性,于是乎,kotlinjs/kotlin native 是没有这一项功能的。

为什么 AutoService 不认识 Kotlin 写的 Processor?

我们写注解处理器,需要编写一个配置文件让编译器知道哪个是注解处理器的入口:

大家看到图中这个文件是红色的,在 IntelliJ 当中红色的目录都是编译生成的,所以这个文件对于偷懒的人来说也根本不会去手写它,而是用 AutoService

@AutoService(Processor.class)
public class AJavaProcessor extends AbstractProcessor {
    ...
}

当然,这个需要引入依赖的:

implementation 'com.google.auto.service:auto-service:1.0-rc4'

其实这货呢,也是一个注解处理器,帮我们在编译的时候生成注解处理器相应的配置文件。

需要注意的是,如果你的注解处理器入口代码是用 Kotlin 写的,那么 AutoService 就傻了。

@AutoService(Processor::class)
class AKotlinProcessor: AbstractProcessor() {
    ...
}

为什么呢?显然直接通过上面的这种依赖方式,只会让 Javac 知道有这么个注解处理器,而 Javac 哪里知道还有什么叫 Kotlin 的东西啊,所以我们还得让 kapt 知道才行。

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

dependencies {
    ...
    kapt 'com.google.auto.service:auto-service:1.0-rc4'
    implementation 'com.google.auto.service:auto-service:1.0-rc4'
    ...
}

首先我们要添加 Kotlin 的各种插件,然后在依赖当中用 kapt 引入google 的 AutoService,又由于 AutoService 中的注解依赖也在这个包里,所以我们还是要把它添加到运行时依赖的(kapt 下面 implementation 那句)。

有了上面的配置,那么我们首先就会在前面提到的 build/tmp/kapt3/stubs 目录中找到我们用 Kotlin 编写的代码转成的 Java 代码,其次 AutoService 生成的注解处理器的配置也会跑到 kapt3/classes 中(原来是在 build/classes/java/main 中)

如何在注解处理器内识别 Kotlin 代码

既然都是 Java 文件,那么我怎么在注解处理器内识别出来哪些代码是 Java 的,哪些是 Kotlin 的呢?其实这个也不难,对比一下就知道了,给大家看一个例子,我有一个 Kotlin 写的类:

class Hello {
}

生成的 stub 长这样:

@kotlin.Metadata(mv = {1, 1, 9}, bv = {1, 0, 2}, k = 1, d1 = {"..."}, d2 = {"Lcom/bennyhuo/activitybuilder/Hello;", "", "()V", "app_debug"})
public final class Hello {

    public Hello() {
        super();
    }
}

哈哈,一眼就看出来,那个注解,什么鬼,Java 源码肯定不会有的。所以要识别你所处理的类是不是 Kotlin 编写的,只需要:

Metadata metadata = typeElement.getAnnotation(Metadata.class);
//如果有这个注解,说明就是 Kotlin 类。
boolean isKotlin = metadata != null;

一旦能够识别出来注解标注的类是 Kotlin,那么我们就可以采用一些 Kotlin Style 的方式生成代码,例如本来如果是 Java 源码,我会生成这样的一个方法:

public class HelloHelper{
    public static void toHelloString(Hello hello){
        ...
    }
}

如果我处理的是 Kotlin 源码,我完全可以生成一个扩展方法让 Kotlin 开发者更愉快地调用:

fun Hello.toHelloString(){
    ...
}

当然,这个扩展方法也是可以被 Java 开发者很愉快地调用的。

注意 Kotlin 的类型

我们一再提到注解处理器只认识 Java,所以就算你用 Kotlin 定义了一个方法如下:

fun hello(a: Int, b: String){
    ...
}

如果我们用注解处理器处理它的时候,参数 a 的类型就会变成 Java 的 int.class 或者 Integer.class,而参数 b 的类型则会变成 java.lang.String,注意不是 kotlin.String

如果你要根据这些类型对应地去生成代码,你需要将这些类型做映射,例如:

java.lang.String -> kotlin.String
java.lang.Integer -> kotlin.Int
int -> kotlin.Int

这个要怎么办呢?不能怎么办,连 J 神的 Kotlin Poet 都没有做这件事儿,如果我们需要写注解处理器生成 Kotlin 的代码,这一点你需要自己来处理。不过呢,我可以给大家一点儿提示,实际上这个类型转换 Kotlin 编译器是做了的,具体可以参考编译器源码:

object JavaToKotlinClassMap : PlatformToKotlinClassMap {

    private val javaToKotlin = HashMap<FqNameUnsafe, ClassId>()

    ...    
}

这个 HashMap 当中就存放了需要映射的类型。

怎么生成 Kotlin 源码?

其实我们前面提到了,用 J 神的 Kotlin Poet 这个项目生成 Kotlin 源码的体验几乎与 Java Poet 没差。不过呢,这个项目目前还只是发到了 0.6,所以难免有个小 bug 啥的,例如我要生成一个匿名内部类,就算我只实现了一个接口,它也会给我添加一个构造方法调用的括号:

object: SomeInterface(){
    ...
}

这样是不对滴。不过这个问题呢,显然也不是什么大问题,已经有大神给了 fix:

Correcting handling of super-classes/interfaces on anonymous classes

https://github.com/square/kotlinpoet/pull/316

由于这个库目前还不算太成熟,参考资料不多,所以如果你想要用,最好去参考一下其中的 test case 来了解其用法。

小结

简单来说,为 Kotlin 提供 apt 服务,无论从编译器(kapt)还是从注解处理器的开发者来讲,你必须都得装作你写的和用的都是 Java 才行。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-02-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Kotlin 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是注解处理器
  • 为什么 AutoService 不认识 Kotlin 写的 Processor?
  • 如何在注解处理器内识别 Kotlin 代码
  • 注意 Kotlin 的类型
  • 怎么生成 Kotlin 源码?
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档