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 until 10) { | total += i |} |""".trimMargin()) .build() ``` Which generates this: ```kotlin fun main() { var total = 0 for (i in 0 until 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 until 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 until $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 until 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) { 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 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() ``` 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 class HelloWorld { fun beyond(): List { val result = ArrayList() result += Hoverboard() result += Hoverboard() result += Hoverboard() return result } fun printThings(things: Array) { println(things) } } ``` #### 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, 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 until %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() 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. ### 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) { strings.sortedWith(object : Comparator { 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 = Map> typealias Predicate = (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:") { 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()`](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>() // 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 com.squareup kotlinpoet [version] ``` 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