[Kotlin翻译]用kotlinpoet进行Kotlin元编程

824 阅读3分钟

原文地址:kotlin.christmas/2020/14

原文作者:kotlin.christmas/

发布时间:2020年12月14日

如果你能自动编写代码,那不是很好吗。嗯,这就是我们今天要看的。元编程,代码生成,或者简而言之;编写kotlin代码,生成更多的kotlin代码🤯。

免责声明:本文专注于源代码生成,不对byte buddy、asm、javaassist或其他进行任何比较。

今天我们来看看kotlinpoet和代码生成提供的一些可能性。

在它最简单的形式下,代码生成可以简单地成为任何程序,当执行时吐出有效的源代码。举个例子,一个简单的函数,返回一个打印语句。

fun createHello(who: String) = "println(\"Hello, $who\")"

createHello("world") // return println("Hello, world")

这里没有什么了不起的事情发生。当它被执行时,它打印出了一些有效的代码,我们以后可能会用到。但是,让我们把它变得更复杂,同时介绍一下今天的明星Kotlinpoet。

val packageName = "com.christmas.kotlin"
val className = "Code"

val cls = TypeSpec
  .classBuilder(className)

val file = FileSpec.get(packageName, cls.build())

println(file)

// ---- Returns ----

package com.christmas.kotlin

public class Code

Kotlinpoet在上面的例子中并没有做什么,但我们会看到它将进一步帮助我们。FileSpec和TypeSpec是kotlinpoet对文件和类的表示,一旦我们有了TypeSpec,我们就可以添加更多的东西,比如属性、构造函数和其他功能。

为了给我们的代码类添加一个简单的属性,我们使用定义一个PropertySpec并把它添加到我们已经存在的TypeSpec中。

val cls = TypeSpec
  .classBuilder(className)
  .addProperty(PropertySpec
    // MUTABLE_LIST and STRING are predefined TypeName's included in kotlinpoet
    .builder("statements", MUTABLE_LIST.parameterizedBy(STRING))
    .initializer("mutableListOf()")
    .build()
  )

// ---- Returns ----
package com.christmas.kotlin

import kotlin.String
import kotlin.collections.MutableList

public class Code {
  public val statements: MutableList<String> = mutableListOf()
}

突然间,我们的类有了一个属性和一些导入语句。Kotlinpoet一直在跟踪哪些导入语句是需要的,以使我们的代码工作,而不需要我们明确编程。这时的复杂性有些有限,但即使现在我们也能从库中得到一些帮助。

在我们的类中添加一个函数,也是以类似的方式继续。

val cls = TypeSpec.classBuilder(className)
  // .addProperty(...)
  .addFunction(FunSpec
    .builder("addStatement")
    .returns(ClassName(packageName, className))
    .addParameter("statement", STRING)
    .addStatement("this.statements += statement")
    .addStatement("return this")
    .build()
  )

// ---- Returns ----
package com.christmas.kotlin

import kotlin.String
import kotlin.collections.MutableList

public class Code {
  public val statements: MutableList<String> = mutableListOf()

  public fun addStatement(statement: String): Code {
    this.statements += statement
    return this
  }
}

Kotlinpoet正确地识别出Code类在我们的类中是可见的,因此没有添加导入或使用该类的全称。然而,如果我们把我们的函数的返回类型改为LONG,它就会把它作为一个导入包括在内。因此,它也会产生不能编译的代码,因为我们的函数的返回类型是this,而不是Long,whoopsi。幸运的是,我们正在生成kotlin源代码,而kotlin编译器可能会告诉我们有问题。

为了得到一个小的类,这似乎是相当多的工作。虽然这可能是真的,但它也为自动处理一些非常酷的东西提供了可能性。通过查看mvnrepository上kotlinpoet的使用情况,我们可以看到它被Arrow(Arrow-Meta)Microsoft Thrifty编译器插件apollo-compilerExpediaGroup的graphql-kotlin使用。


通过www.DeepL.com/Translator(免费版)翻译