跳过内容

函数

以上所有函数都有代码体。使用 KModifier.ABSTRACT 可以得到一个没有代码体的函数。这仅在函数包含在抽象类或接口中时才合法。

val flux = FunSpec.builder("flux")
  .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
  .build()

val helloWorld = TypeSpec.classBuilder("HelloWorld")
  .addModifiers(KModifier.ABSTRACT)
  .addFunction(flux)
  .build()

这会生成如下代码

abstract class HelloWorld {
  protected abstract fun flux()
}

其他修饰符在允许的情况下都有效。

方法(Functions)还包含参数、可变参数 (varargs)、KDoc、注解、类型变量、返回类型以及扩展函数的接收者类型。所有这些都通过 FunSpec.Builder 进行配置。

扩展函数

通过指定 receiver 可以生成扩展函数。

val square = FunSpec.builder("square")
  .receiver(Int::class)
  .returns(Int::class)
  .addStatement("var s = this * this")
  .addStatement("return s")
  .build()

输出如下

fun Int.square(): Int {
  val s = this * this
  return s
}

单表达式函数

KotlinPoet 可以识别单表达式函数并正确打印。它将每个代码体以 return 开头的函数视为单表达式函数。

val abs = FunSpec.builder("abs")
  .addParameter("x", Int::class)
  .returns(Int::class)
  .addStatement("return if (x < 0) -x else x")
  .build()

输出如下

fun abs(x: Int): Int = if (x < 0) -x else x

默认函数参数

考虑下面的例子。函数参数 b 的默认值为 0,以避免函数重载。

fun add(a: Int, b: Int = 0) {
  print("a + b = ${a + b}")
}

使用 defaultValue() 构建器函数声明函数参数的默认值。

FunSpec.builder("add")
  .addParameter("a", Int::class)
  .addParameter(
    ParameterSpec.builder("b", Int::class)
      .defaultValue("%L", 0)
      .build()
  )
  .addStatement("print(\"a + b = ${a + b}\")")
  .build()

换行是明确的

为了保证代码的正确性,从 2.0 版本开始,即使代码行超出长度限制,KotlinPoet 也不会将代码块中的空格替换为换行符。例如,我们来看这个函数

val funSpec = FunSpec.builder("foo")
  .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
  .build()

该函数将始终像这样打印出来

public fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }

虽然输出是正确的,但生成的代码行相当长且难以阅读。KotlinPoet 无法理解表达式的上下文并为你调整格式,但你可以使用一个技巧来声明一个断行空间——在你确定可以安全地选择换行的地方使用 符号。让我们将此应用于我们的示例

val funSpec = FunSpec.builder("foo")
  .addStatement("return (100..10000).map { number ->♢number * number♢}.map { number ->♢number.toString()♢}.also { string ->♢println(string)♢}")
  .build()

现在这将产生以下结果

public fun foo(): Unit = (100..10000).map { number -> number * number }.map { number ->
  number.toString() }.also { string -> println(string) }

这稍微好一些,但远非完美——你可以随意尝试其他格式修饰符,例如 KotlinPoet 支持的标准 \n 字符,或者库自带的缩进格式修饰符()(有关更多信息,请参阅 CodeBlock 的文档)。

最后,不完美的格式是该库的一个已知限制,因为 KotlinPoet 在设计上优先考虑生成代码的正确性而非风格。如果正确且整洁的格式对你的使用场景很重要,请考虑使用专用的代码格式化工具对 KotlinPoet 的输出进行后处理。