blob: 683f40e649667e039e86f7ee3e501b472758626e [file] [log] [blame] [view] [edit]
KotlinPoet
==========
`KotlinPoet` is a Kotlin and Java API for generating `.kt` source files.
Source file generation can be useful when doing things such as annotation processing or interacting
with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate
the need to write boilerplate while also keeping a single source of truth for the metadata.
### Example
Here's a `HelloWorld` file:
```kotlin
class Greeter(val name: String) {
fun greet() {
println("""Hello, $name""")
}
}
fun main(vararg args: String) {
Greeter(args[0]).greet()
}
```
And this is the code to generate it with KotlinPoet:
```kotlin
val greeterClass = ClassName("", "Greeter")
val file = FileSpec.builder("", "HelloWorld")
.addType(
TypeSpec.classBuilder("Greeter")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("name", String::class)
.build()
)
.addProperty(
PropertySpec.builder("name", String::class)
.initializer("name")
.build()
)
.addFunction(
FunSpec.builder("greet")
.addStatement("println(%P)", "Hello, \$name")
.build()
)
.build()
)
.addFunction(
FunSpec.builder("main")
.addParameter("args", String::class, VARARG)
.addStatement("%T(args[0]).greet()", greeterClass)
.build()
)
.build()
file.writeTo(System.out)
```
The [KDoc][kdoc] catalogs the complete KotlinPoet API, which is inspired by [JavaPoet][javapoet].
**Note:** In order to maximize portability, KotlinPoet generates code with explicit visibility
modifiers. This ensures compatibility with both standard Kotlin projects as well as projects
using [explicit API mode](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors).
Examples in this file omit those modifiers for brevity.
### Code & Control Flow
Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining
and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`),
classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`),
properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and
annotations (`AnnotationSpec`).
But the _body_ of methods and constructors is not modeled. There's no expression class, no
statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can
take advantage of Kotlin's multiline strings to make this look nice:
```kotlin
val main = FunSpec.builder("main")
.addCode("""
|var total = 0
|for (i in 0..<10) {
| total += i
|}
|""".trimMargin())
.build()
```
Which generates this:
```kotlin
fun main() {
var total = 0
for (i in 0..<10) {
total += i
}
}
```
There are additional APIs to assist with newlines, braces and indentation:
```kotlin
val main = FunSpec.builder("main")
.addStatement("var total = 0")
.beginControlFlow("for (i in 0..<10)")
.addStatement("total += i")
.endControlFlow()
.build()
```
This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10,
we want to make the operation and range configurable. Here's a method that generates a method:
```kotlin
private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
return FunSpec.builder(name)
.returns(Int::class)
.addStatement("var result = 1")
.beginControlFlow("for (i in $from..<$to)")
.addStatement("result = result $op i")
.endControlFlow()
.addStatement("return result")
.build()
}
```
And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`:
```kotlin
fun multiply10to20(): kotlin.Int {
var result = 1
for (i in 10..<20) {
result = result * i
}
return result
}
```
Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can
read through it to make sure it's right.
### %S for Strings
When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete
with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which
returns its own name:
```kotlin
fun main(args: Array<String>) {
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addFunction(whatsMyNameYo("slimShady"))
.addFunction(whatsMyNameYo("eminem"))
.addFunction(whatsMyNameYo("marshallMathers"))
.build()
val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
.addType(helloWorld)
.build()
kotlinFile.writeTo(System.out)
}
private fun whatsMyNameYo(name: String): FunSpec {
return FunSpec.builder(name)
.returns(String::class)
.addStatement("return %S", name)
.build()
}
```
In this case, using `%S` gives us quotation marks:
```kotlin
class HelloWorld {
fun slimShady(): String = "slimShady"
fun eminem(): String = "eminem"
fun marshallMathers(): String = "marshallMathers"
}
```
### %P for String Templates
`%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string
templates, which may fail to compile in generated code:
```kotlin
val stringWithADollar = "Your total is " + "$" + "50"
val funSpec = FunSpec.builder("printTotal")
.returns(String::class)
.addStatement("return %S", stringWithADollar)
.build()
```
produces:
```kotlin
fun printTotal(): String = "Your total is ${'$'}50"
```
If you need to generate string templates, use `%P`, which doesn't escape dollars:
```kotlin
val amount = 50
val stringWithADollar = "Your total is " + "$" + "amount"
val funSpec = FunSpec.builder("printTotal")
.returns(String::class)
.addStatement("return %P", stringWithADollar)
.build()
```
produces:
```kotlin
fun printTotal(): String = "Your total is $amount"
```
You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference
importable types or members inside the string template:
```kotlin
val file = FileSpec.builder("com.example", "Digits")
.addFunction(
FunSpec.builder("print")
.addParameter("digits", IntArray::class)
.addStatement("println(%P)", buildCodeBlock {
val contentToString = MemberName("kotlin.collections", "contentToString")
add("These are the digits: \${digits.%M()}", contentToString)
})
.build()
)
.build()
println(file)
```
The snippet above will produce the following output, handling the imports properly:
```kotlin
package com.example
import kotlin.IntArray
import kotlin.collections.contentToString
fun print(digits: IntArray) {
println("""These are the digits: ${digits.contentToString()}""")
}
```
### %T for Types
KotlinPoet has rich built-in support for types, including automatic generation of `import`
statements. Just use **`%T`** to reference **types**:
```kotlin
val today = FunSpec.builder("today")
.returns(Date::class)
.addStatement("return %T()", Date::class)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addFunction(today)
.build()
val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
.addType(helloWorld)
.build()
kotlinFile.writeTo(System.out)
```
That generates the following `.kt` file, complete with the necessary `import`:
```kotlin
package com.example.helloworld
import java.util.Date
class HelloWorld {
fun today(): Date = Date()
}
```
We passed `Date::class` to reference a class that just-so-happens to be available when we're
generating code. This doesn't need to be the case. Here's a similar example, but this one
references a class that doesn't exist (yet):
```kotlin
val hoverboard = ClassName("com.mattel", "Hoverboard")
val tomorrow = FunSpec.builder("tomorrow")
.returns(hoverboard)
.addStatement("return %T()", hoverboard)
.build()
```
And that not-yet-existent class is imported as well:
```kotlin
package com.example.helloworld
import com.mattel.Hoverboard
class HelloWorld {
fun tomorrow(): Hoverboard = Hoverboard()
}
```
The `ClassName` type is very important, and you'll need it frequently when you're using KotlinPoet.
It can identify any _declared_ class. Declared types are just the beginning of Kotlin's rich type
system: we also have arrays, parameterized types, wildcard types, lambda types and type variables.
KotlinPoet has classes for building each of these:
```kotlin
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.STAR
val hoverboard = ClassName("com.mattel", "Hoverboard")
val list = ClassName("kotlin.collections", "List")
val arrayList = ClassName("kotlin.collections", "ArrayList")
val listOfHoverboards = list.parameterizedBy(hoverboard)
val arrayListOfHoverboards = arrayList.parameterizedBy(hoverboard)
val thing = ClassName("com.misc", "Thing")
val array = ClassName("kotlin", "Array")
val producerArrayOfThings = array.parameterizedBy(WildcardTypeName.producerOf(thing))
val beyond = FunSpec.builder("beyond")
.returns(listOfHoverboards)
.addStatement("val result = %T()", arrayListOfHoverboards)
.addStatement("result += %T()", hoverboard)
.addStatement("result += %T()", hoverboard)
.addStatement("result += %T()", hoverboard)
.addStatement("return result")
.build()
val printThings = FunSpec.builder("printThings")
.addParameter("things", producerArrayOfThings)
.addStatement("println(things)")
.build()
val printKClass = FunSpec.builder("printKClass")
.addParameter("kClass", KClass::class.asClassName().parameterizedBy(STAR))
.addStatement("println(kClass)")
.build()
```
The `STAR` is represented as `*` in KotlinPoet. You can find more in the [KDoc][kdoc].
KotlinPoet will decompose each type and import its components where possible.
```kotlin
package com.example.helloworld
import com.mattel.Hoverboard
import com.misc.Thing
import kotlin.Array
import kotlin.collections.ArrayList
import kotlin.collections.List
import kotlin.reflect.KClass
class HelloWorld {
fun beyond(): List<Hoverboard> {
val result = ArrayList<Hoverboard>()
result += Hoverboard()
result += Hoverboard()
result += Hoverboard()
return result
}
fun printThings(things: Array<out Thing>) {
println(things)
}
fun printKClass(kClass: KClass<*>) {
println(kClass)
}
}
```
#### Nullable Types
KotlinPoet supports nullable types. To convert a `TypeName` into its nullable counterpart, use the
`copy()` method with `nullable` parameter set to `true`:
```kotlin
val java = PropertySpec.builder("java", String::class.asTypeName().copy(nullable = true))
.mutable()
.addModifiers(KModifier.PRIVATE)
.initializer("null")
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addProperty(java)
.addProperty("kotlin", String::class, KModifier.PRIVATE)
.build()
```
generates:
```kotlin
class HelloWorld {
private var java: String? = null
private val kotlin: String
}
```
### %M for Members
Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties),
which comes handy when your code needs to access top-level members and members declared inside
objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the
placeholder, and KotlinPoet will handle imports automatically:
```kotlin
val createTaco = MemberName("com.squareup.tacos", "createTaco")
val isVegan = MemberName("com.squareup.tacos", "isVegan")
val file = FileSpec.builder("com.squareup.example", "TacoTest")
.addFunction(
FunSpec.builder("main")
.addStatement("val taco = %M()", createTaco)
.addStatement("println(taco.%M)", isVegan)
.build()
)
.build()
println(file)
```
The code above generates the following file:
```kotlin
package com.squareup.example
import com.squareup.tacos.createTaco
import com.squareup.tacos.isVegan
fun main() {
val taco = createTaco()
println(taco.isVegan)
}
```
As you can see, it's also possible to use `%M` to reference extension functions and properties. You
just need to make sure the member can be imported without simple name collisions, otherwise
importing will fail and the code generator output will not pass compilation. There's a way to work
around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing
`MemberName`:
```kotlin
val createTaco = MemberName("com.squareup.tacos", "createTaco")
val createCake = MemberName("com.squareup.cakes", "createCake")
val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
val file = FileSpec.builder("com.squareup.example", "Test")
.addAliasedImport(isTacoVegan, "isTacoVegan")
.addAliasedImport(isCakeVegan, "isCakeVegan")
.addFunction(
FunSpec.builder("main")
.addStatement("val taco = %M()", createTaco)
.addStatement("val cake = %M()", createCake)
.addStatement("println(taco.%M)", isTacoVegan)
.addStatement("println(cake.%M)", isCakeVegan)
.build()
)
.build()
println(file)
```
KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`:
```kotlin
package com.squareup.example
import com.squareup.cakes.createCake
import com.squareup.tacos.createTaco
import com.squareup.cakes.isVegan as isCakeVegan
import com.squareup.tacos.isVegan as isTacoVegan
fun main() {
val taco = createTaco()
val cake = createCake()
println(taco.isTacoVegan)
println(cake.isCakeVegan)
}
```
#### MemberName and operators
MemberName also supports operators, you can use `MemberName(String, KOperator)`
or `MemberName(ClassName, KOperator)` to import and reference operators.
```kotlin
val taco = ClassName("com.squareup.tacos", "Taco")
val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
val file = FileSpec.builder("com.example", "Test")
.addFunction(
FunSpec.builder("makeTacoHealthy")
.addParameter("taco", taco)
.beginControlFlow("for (ingredient %M taco)", iterator)
.addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
.endControlFlow()
.addStatement("return taco")
.build()
)
.build()
println(file)
```
KotlinPoet will import the extension operator functions and emit the operator.
```kotlin
package com.example
import com.squareup.tacos.Taco
import com.squareup.tacos.ingredient.Meat
import com.squareup.tacos.internal.iterator
import com.squareup.tacos.internal.minusAssign
fun makeTacoHealthy(taco: Taco) {
for (ingredient in taco) {
if (ingredient is Meat) taco -= ingredient
}
return taco
}
```
### %N for Names
Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by
its name. Here's a method that calls another:
```kotlin
fun byteToHex(b: Int): String {
val result = CharArray(2)
result[0] = hexDigit((b ushr 4) and 0xf)
result[1] = hexDigit(b and 0xf)
return String(result)
}
fun hexDigit(i: Int): Char {
return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()
}
```
When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()`
method using `%N`:
```kotlin
val hexDigit = FunSpec.builder("hexDigit")
.addParameter("i", Int::class)
.returns(Char::class)
.addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()")
.build()
val byteToHex = FunSpec.builder("byteToHex")
.addParameter("b", Int::class)
.returns(String::class)
.addStatement("val result = CharArray(2)")
.addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit)
.addStatement("result[1] = %N(b and 0xf)", hexDigit)
.addStatement("return String(result)")
.build()
```
Another handy feature that `%N` provides is automatically escaping names that contain illegal
identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin
keyword as the simple name:
```kotlin
val taco = ClassName("com.squareup.tacos", "Taco")
val packager = ClassName("com.squareup.tacos", "TacoPackager")
val file = FileSpec.builder("com.example", "Test")
.addFunction(
FunSpec.builder("packageTacos")
.addParameter("tacos", LIST.parameterizedBy(taco))
.addParameter("packager", packager)
.addStatement("packager.%N(tacos)", packager.member("package"))
.build()
)
.build()
```
`%N` will escape the name for you, ensuring that the output will pass compilation:
```kotlin
package com.example
import com.squareup.tacos.Taco
import com.squareup.tacos.TacoPackager
import kotlin.collections.List
fun packageTacos(tacos: List<Taco>, packager: TacoPackager) {
packager.`package`(tacos)
}
```
### %L for Literals
Although Kotlin's string templates usually work well in cases when you want to include literals into
generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with
[`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This
works just like `Formatter`'s `%s`:
```kotlin
private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
return FunSpec.builder(name)
.returns(Int::class)
.addStatement("var result = 0")
.beginControlFlow("for (i in %L..<%L)", from, to)
.addStatement("result = result %L i", op)
.endControlFlow()
.addStatement("return result")
.build()
}
```
Literals are emitted directly to the output code with no escaping. Arguments for literals may be
strings, primitives, and a few KotlinPoet types described below.
### Code block format strings
Code blocks may specify the values for their placeholders in a few ways. Only one style may be used
for each operation on a code block.
#### Relative Arguments
Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each
example, we generate code to say "I ate 3 tacos"
```kotlin
CodeBlock.builder().add("I ate %L %L", 3, "tacos")
```
#### Positional Arguments
Place an integer index (1-based) before the placeholder in the format string to specify which
argument to use.
```kotlin
CodeBlock.builder().add("I ate %2L %1L", "tacos", 3)
```
#### Named Arguments
Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()`
with a map containing all argument keys in the format string. Argument names use characters in
`a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character.
```kotlin
val map = LinkedHashMap<String, Any>()
map += "food" to "tacos"
map += "count" to 3
CodeBlock.builder().addNamed("I ate %count:L %food:L", map)
```
### Functions
All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
body. This is only legal if it is enclosed by an abstract class or an interface.
```kotlin
val flux = FunSpec.builder("flux")
.addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(KModifier.ABSTRACT)
.addFunction(flux)
.build()
```
Which generates this:
```kotlin
abstract class HelloWorld {
protected abstract fun flux()
}
```
The other modifiers work where permitted.
Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
type for extension functions. All of these are configured with `FunSpec.Builder`.
#### Extension functions
Extension functions can be generated by specifying a `receiver`.
```kotlin
val square = FunSpec.builder("square")
.receiver(Int::class)
.returns(Int::class)
.addStatement("var s = this * this")
.addStatement("return s")
.build()
```
Which outputs:
```kotlin
fun Int.square(): Int {
val s = this * this
return s
}
```
#### Single-expression functions
KotlinPoet can recognize single-expression functions and print them out properly. It treats
each function with a body that starts with `return` as a single-expression function:
```kotlin
val abs = FunSpec.builder("abs")
.addParameter("x", Int::class)
.returns(Int::class)
.addStatement("return if (x < 0) -x else x")
.build()
```
Which outputs:
```kotlin
fun abs(x: Int): Int = if (x < 0) -x else x
```
#### Default function arguments
Consider the example below.
Function argument `b` has a default value of 0 to avoid overloading this function.
```kotlin
fun add(a: Int, b: Int = 0) {
print("a + b = ${a + b}")
}
```
Use the `defaultValue()` builder function to declare default value for a function argument.
```kotlin
FunSpec.builder("add")
.addParameter("a", Int::class)
.addParameter(
ParameterSpec.builder("b", Int::class)
.defaultValue("%L", 0)
.build()
)
.addStatement("print(\"a + b = ${a + b}\")")
.build()
```
#### Spaces wrap by default!
In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
function for example:
```kotlin
val funSpec = FunSpec.builder("foo")
.addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
.build()
```
Depending on where it's found in the file, it may end up being printed out like this:
```kotlin
fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
{ string -> println(string) }
```
Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
otherwise use a space. Let's apply this to our example:
```kotlin
val funSpec = FunSpec.builder("foo")
.addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
.build()
```
This will now produce the following result:
```kotlin
fun foo() = (100..10000).map { number -> number * number }.map { number ->
number.toString()
}.also { string -> println(string) }
```
The code is now correct and will compile properly. It still doesn't look perfect - you can play with
replacing other spaces in the code block with `·` symbols to achieve better formatting.
Another common use case where you'd want to ensure spaces don't wrap is when emitting string literals:
```kotlin
CodeBlock.of("""println("Class: $className")""")
```
If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
```kotlin
println("Class:
very.long.class.name.Here")
```
KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space inside of it
should never be wrapped. To make sure this case is handled correctly, use the `%S` modifier (as described in
[%S for Strings](#s-for-strings)):
```kotlin
CodeBlock.of("""println(%S)""", "Class: $className")
```
Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
### Constructors
`FunSpec` is a slight misnomer; it can also be used for constructors:
```kotlin
val flux = FunSpec.constructorBuilder()
.addParameter("greeting", String::class)
.addStatement("this.%N = %N", "greeting", "greeting")
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addProperty("greeting", String::class, KModifier.PRIVATE)
.addFunction(flux)
.build()
```
Which generates this:
```kotlin
class HelloWorld {
private val greeting: String
constructor(greeting: String) {
this.greeting = greeting
}
}
```
For the most part, constructors work just like methods. When emitting code, KotlinPoet will place
constructors before methods in the output file.
Often times you'll need to generate the primary constructor for a class:
```kotlin
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.primaryConstructor(flux)
.addProperty("greeting", String::class, KModifier.PRIVATE)
.build()
```
This code, however, generates the following:
```kotlin
class HelloWorld(greeting: String) {
private val greeting: String
init {
this.greeting = greeting
}
}
```
By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share
the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized
via the constructor parameter:
```kotlin
val flux = FunSpec.constructorBuilder()
.addParameter("greeting", String::class)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.primaryConstructor(flux)
.addProperty(
PropertySpec.builder("greeting", String::class)
.initializer("greeting")
.addModifiers(KModifier.PRIVATE)
.build()
)
.build()
```
Now we're getting the following output:
```kotlin
class HelloWorld(private val greeting: String)
```
Notice that KotlinPoet omits `{}` for classes with empty bodies.
### Parameters
Declare parameters on methods and constructors with either `ParameterSpec.builder()` or
`FunSpec`'s convenient `addParameter()` API:
```kotlin
val android = ParameterSpec.builder("android", String::class)
.defaultValue("\"pie\"")
.build()
val welcomeOverlords = FunSpec.builder("welcomeOverlords")
.addParameter(android)
.addParameter("robot", String::class)
.build()
```
The code above generates:
```kotlin
fun welcomeOverlords(android: String = "pie", robot: String) {
}
```
The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`).
### Properties
Like parameters, properties can be created either with builders or by using convenient helper
methods:
```kotlin
val android = PropertySpec.builder("android", String::class)
.addModifiers(KModifier.PRIVATE)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addProperty(android)
.addProperty("robot", String::class, KModifier.PRIVATE)
.build()
```
Which generates:
```kotlin
class HelloWorld {
private val android: String
private val robot: String
}
```
The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
blocks above:
```kotlin
val android = PropertySpec.builder("android", String::class)
.addModifiers(KModifier.PRIVATE)
.initializer("%S + %L", "Oreo v.", 8.1)
.build()
```
Which generates:
```kotlin
private val android: String = "Oreo v." + 8.1
```
By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
`var`:
```kotlin
val android = PropertySpec.builder("android", String::class)
.mutable()
.addModifiers(KModifier.PRIVATE)
.initializer("%S + %L", "Oreo v.", 8.1)
.build()
```
#### Inline properties
The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
```kotlin
val android = PropertySpec.builder("android", String::class)
.mutable()
.addModifiers(KModifier.INLINE)
.build()
```
will produce an error:
```
java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
properties. You should mark either the getter, the setter, or both inline.
```
Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
the compiler. Let's add a getter to this property:
```kotlin
val android = PropertySpec.builder("android", String::class)
.mutable()
.getter(
FunSpec.getterBuilder()
.addModifiers(KModifier.INLINE)
.addStatement("return %S", "foo")
.build()
)
.build()
```
The result is the following:
```kotlin
var android: kotlin.String
inline get() = "foo"
```
Now, what if we wanted to add a non-inline setter to the property above? We can do so without
modifying any of the code we wrote previously:
```kotlin
val android = PropertySpec.builder("android", String::class)
.mutable()
.getter(
FunSpec.getterBuilder()
.addModifiers(KModifier.INLINE)
.addStatement("return %S", "foo")
.build()
)
.setter(
FunSpec.setterBuilder()
.addParameter("value", String::class)
.build()
)
.build()
```
We get the expected result:
```kotlin
var android: kotlin.String
inline get() = "foo"
set(`value`) {
}
```
Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
produce the following result:
```kotlin
inline var android: kotlin.String
get() = "foo"
set(`value`) {
}
```
Removing the modifier from either the getter or the setter will unwrap the expression back.
If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
would have had to manually add/remove the modifier whenever the state of the accessors changes in
order to get correct and compilable output. We're solving this problem by making accessors the
source of truth for the `inline` modifier.
### Interfaces
KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`.
The modifier is necessary when defining the interface:
```kotlin
val helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
.addProperty("buzz", String::class)
.addFunction(
FunSpec.builder("beep")
.addModifiers(KModifier.ABSTRACT)
.build()
)
.build()
```
But these modifiers are omitted when the code is generated. These are the default so we don't need
to include them for `kotlinc`'s benefit!
```kotlin
interface HelloWorld {
val buzz: String
fun beep()
}
```
Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in
KotlinPoet, use `TypeSpec.funInterfaceBuilder()`.
```kotlin
val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld")
.addFunction(
FunSpec.builder("beep")
.addModifiers(KModifier.ABSTRACT)
.build()
)
.build()
// Generates...
fun interface HelloWorld {
fun beep()
}
```
### Objects
KotlinPoet supports objects:
```kotlin
val helloWorld = TypeSpec.objectBuilder("HelloWorld")
.addProperty(
PropertySpec.builder("buzz", String::class)
.initializer("%S", "buzz")
.build()
)
.addFunction(
FunSpec.builder("beep")
.addStatement("println(%S)", "Beep!")
.build()
)
.build()
```
Similarly, you can create companion objects and add them to classes using `addType()`:
```kotlin
val companion = TypeSpec.companionObjectBuilder()
.addProperty(
PropertySpec.builder("buzz", String::class)
.initializer("%S", "buzz")
.build()
)
.addFunction(
FunSpec.builder("beep")
.addStatement("println(%S)", "Beep!")
.build()
)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addType(companion)
.build()
```
You can provide an optional name for a companion object.
### Enums
Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value:
```kotlin
val helloWorld = TypeSpec.enumBuilder("Roshambo")
.addEnumConstant("ROCK")
.addEnumConstant("SCISSORS")
.addEnumConstant("PAPER")
.build()
```
To generate this:
```kotlin
enum class Roshambo {
ROCK,
SCISSORS,
PAPER
}
```
Fancy enums are supported, where the enum values override methods or call a superclass constructor.
Here's a comprehensive example:
```kotlin
val helloWorld = TypeSpec.enumBuilder("Roshambo")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("handsign", String::class)
.build()
)
.addEnumConstant(
"ROCK", TypeSpec.anonymousClassBuilder()
.addSuperclassConstructorParameter("%S", "fist")
.addFunction(
FunSpec.builder("toString")
.addModifiers(KModifier.OVERRIDE)
.addStatement("return %S", "avalanche!")
.returns(String::class)
.build()
)
.build()
)
.addEnumConstant(
"SCISSORS", TypeSpec.anonymousClassBuilder()
.addSuperclassConstructorParameter("%S", "peace")
.build()
)
.addEnumConstant(
"PAPER", TypeSpec.anonymousClassBuilder()
.addSuperclassConstructorParameter("%S", "flat")
.build()
)
.addProperty(
PropertySpec.builder("handsign", String::class, KModifier.PRIVATE)
.initializer("handsign")
.build()
)
.build()
```
Which generates this:
```kotlin
enum class Roshambo(private val handsign: String) {
ROCK("fist") {
override fun toString(): String = "avalanche!"
},
SCISSORS("peace"),
PAPER("flat");
}
```
### Anonymous Inner Classes
In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be
used in code blocks. They are values that can be referenced with `%L`:
```kotlin
val comparator = TypeSpec.anonymousClassBuilder()
.addSuperinterface(Comparator::class.parameterizedBy(String::class))
.addFunction(
FunSpec.builder("compare")
.addModifiers(KModifier.OVERRIDE)
.addParameter("a", String::class)
.addParameter("b", String::class)
.returns(Int::class)
.addStatement("return %N.length - %N.length", "a", "b")
.build()
)
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addFunction(
FunSpec.builder("sortByLength")
.addParameter("strings", List::class.parameterizedBy(String::class))
.addStatement("%N.sortedWith(%L)", "strings", comparator)
.build()
)
.build()
```
This generates a method that contains a class that contains a method:
```kotlin
class HelloWorld {
fun sortByLength(strings: List<String>) {
strings.sortedWith(object : Comparator<String> {
override fun compare(a: String, b: String): Int = a.length - b.length
})
}
}
```
One particularly tricky part of defining anonymous inner classes is the arguments to the superclass
constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method.
### Annotations
Simple annotations are easy:
```kotlin
val test = FunSpec.builder("test string equality")
.addAnnotation(Test::class)
.addStatement("assertThat(%1S).isEqualTo(%1S)", "foo")
.build()
```
Which generates this function with an `@Test` annotation:
```kotlin
@Test
fun `test string equality`() {
assertThat("foo").isEqualTo("foo")
}
```
Use `AnnotationSpec.builder()` to set properties on annotations:
```kotlin
val logRecord = FunSpec.builder("recordEvent")
.addModifiers(KModifier.ABSTRACT)
.addAnnotation(
AnnotationSpec.builder(Headers::class)
.addMember("accept = %S", "application/json; charset=utf-8")
.addMember("userAgent = %S", "Square Cash")
.build()
)
.addParameter("logRecord", LogRecord::class)
.returns(LogReceipt::class)
.build()
```
Which generates this annotation with `accept` and `userAgent` properties:
```kotlin
@Headers(
accept = "application/json; charset=utf-8",
userAgent = "Square Cash"
)
abstract fun recordEvent(logRecord: LogRecord): LogReceipt
```
When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded
annotations:
```kotlin
val headerList = ClassName("", "HeaderList")
val header = ClassName("", "Header")
val logRecord = FunSpec.builder("recordEvent")
.addModifiers(KModifier.ABSTRACT)
.addAnnotation(
AnnotationSpec.builder(headerList)
.addMember(
"[\n⇥%L,\n%L⇤\n]",
AnnotationSpec.builder(header)
.addMember("name = %S", "Accept")
.addMember("value = %S", "application/json; charset=utf-8")
.build(),
AnnotationSpec.builder(header)
.addMember("name = %S", "User-Agent")
.addMember("value = %S", "Square Cash")
.build()
)
.build()
)
.addParameter("logRecord", logRecordName)
.returns(logReceipt)
.build()
```
Which generates this:
```kotlin
@HeaderList(
[
Header(name = "Accept", value = "application/json; charset=utf-8"),
Header(name = "User-Agent", value = "Square Cash")
]
)
abstract fun recordEvent(logRecord: LogRecord): LogReceipt
```
KotlinPoet supports use-site targets for annotations:
```kotlin
val utils = FileSpec.builder("com.example", "Utils")
.addAnnotation(
AnnotationSpec.builder(JvmName::class)
.useSiteTarget(UseSiteTarget.FILE)
.build()
)
.addFunction(
FunSpec.builder("abs")
.receiver(Int::class)
.returns(Int::class)
.addStatement("return if (this < 0) -this else this")
.build()
)
.build()
```
Will output this:
```kotlin
@file:JvmName
package com.example
import kotlin.Int
import kotlin.jvm.JvmName
fun Int.abs(): Int = if (this < 0) -this else this
```
### Type Aliases
KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized
types and lambdas:
```kotlin
val k = TypeVariableName("K")
val t = TypeVariableName("T")
val fileTable = Map::class.asClassName()
.parameterizedBy(k, Set::class.parameterizedBy(File::class))
val predicate = LambdaTypeName.get(
parameters = arrayOf(t),
returnType = Boolean::class.asClassName()
)
val helloWorld = FileSpec.builder("com.example", "HelloWorld")
.addTypeAlias(TypeAliasSpec.builder("Word", String::class).build())
.addTypeAlias(
TypeAliasSpec.builder("FileTable", fileTable)
.addTypeVariable(k)
.build()
)
.addTypeAlias(
TypeAliasSpec.builder("Predicate", predicate)
.addTypeVariable(t)
.build()
)
.build()
```
Which generates the following:
```kotlin
package com.example
import java.io.File
import kotlin.Boolean
import kotlin.String
import kotlin.collections.Map
import kotlin.collections.Set
typealias Word = String
typealias FileTable<K> = Map<K, Set<File>>
typealias Predicate<T> = (T) -> Boolean
```
### Callable References
[Callable references](https://kotlinlang.org/docs/reference/reflection.html#callable-references) to
constructors, functions, and properties may be emitted via:
- `ClassName.constructorReference()` for constructors
- `MemberName.reference()` for functions and properties
For example,
```kotlin
val helloClass = ClassName("com.example.hello", "Hello")
val worldFunction: MemberName = helloClass.member("world")
val byeProperty: MemberName = helloClass.nestedClass("World").member("bye")
val factoriesFun = FunSpec.builder("factories")
.addStatement("val hello = %L", helloClass.constructorReference())
.addStatement("val world = %L", worldFunction.reference())
.addStatement("val bye = %L", byeProperty.reference())
.build()
FileSpec.builder("com.example", "HelloWorld")
.addFunction(factoriesFun)
.build()
```
would generate:
```kotlin
package com.example
import com.example.hello.Hello
fun factories() {
val hello = ::Hello
val world = Hello::world
val bye = Hello.World::bye
}
```
Top-level classes and members with conflicting names may require aliased imports, as with
[member names](#m-for-members).
kotlin-reflect
--------
To generate source code from
any [`KType`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/), including
information that's not accessible to the builtin reflection APIs, KotlinPoet depends
on [kotlin-reflect](https://kotlinlang.org/docs/reflection.html#jvm-dependency). `kotlin-reflect`
can read the metadata of your classes and access this extra information. KotlinPoet can for an
example, read the type parameters and
their [variance](https://kotlinlang.org/docs/generics.html#variance) from a generic `KType` and
generate appropriate source code.
`kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove
it from the final executable to save some space and/or simplify the proguard/R8 setup (for example
for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the
KotlinPoet APIs:
```kotlin
dependencies {
implementation("com.squareup:kotlinpoet:<version>") {
exclude(module = "kotlin-reflect")
}
}
```
The main APIs that require `kotlin-reflect`
are [`KType.asTypeName()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html)
and [`typeNameOf<T>()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html).
If you're calling one of these without `kotlin-reflect` in the classpath and the type is generic
or has annotations you will get a crash.
You can replace it with code that passes type parameters or annotations explicitly and doesn't
need `kotlin-reflect`. For example:
```kotlin
// Replace
// kotlin-reflect needed
val typeName = typeNameOf<List<Int?>>()
// With
// kotlin-reflect not needed
val typeName =
List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true))
```
Download
--------
![Maven Central][version-shield]
Download [the latest .jar][dl] or depend via Maven:
```xml
<dependency>
<groupId>com.squareup</groupId>
<artifactId>kotlinpoet</artifactId>
<version>[version]</version>
</dependency>
```
or Gradle:
```groovy
implementation("com.squareup:kotlinpoet:[version]")
```
Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
License
-------
Copyright 2017 Square, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet&v=LATEST
[version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet
[snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/
[kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/
[javapoet]: https://github.com/square/javapoet/
[formatter]: https://developer.android.com/reference/java/util/Formatter.html