kotlinpoet Documentation

A Kotlin API for generating .kt source files.

4,163 stars Kotlin Kotlin 27 files ยท ~25,452 tokens #code-generation#javapoet#kotlin
RAW Doc

Technical Documentation: square/kotlinpoet

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

KotlinPoet

KotlinPoet is a Kotlin and Java API for generating .kt source files.

[square.github.io/kotlinpoet](https://square.github.io/kotlinpoet)

License

text
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.

Index (`docs/index.md`)

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 catalogs the complete KotlinPoet API, which is inspired by 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. Examples in the documentation omit those modifiers for
brevity.

Download

Maven Central

Download the latest .jar or depend via Maven:

xml
<dependency>
  <groupId>com.squareup</groupId>
  <artifactId>kotlinpoet-jvm</artifactId>
  <version>[version]</version>
</dependency>

or Gradle:

groovy
implementation("com.squareup:kotlinpoet:[version]")

Snapshots of the development version are available in the Central Portal Snapshots repository.

License

text
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.

Annotations (`docs/annotations.md`)

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

Annotating Types

KotlinPoet provides a convenient annotated() API for adding annotations to types:

kotlin
// Add a single annotation.
val annotatedType = String::class.asTypeName()
  .annotated(AnnotationSpec.builder(MyAnnotation::class).build())

// Add multiple annotations.
val multiAnnotatedType = Int::class.asTypeName()
  .annotated(
    AnnotationSpec.builder(Suppress::class).addMember("%S", "unused").build(),
    AnnotationSpec.builder(Deprecated::class).addMember("%S", "Use something else").build()
  )

// Add annotations by class.
val simpleAnnotated = String::class.asTypeName().annotated(Suppress::class, Deprecated::class)

// Chain multiple calls.
val chainedAnnotations = String::class.asTypeName()
  .annotated(Suppress::class)
  .annotated(Deprecated::class)

This is especially useful when working with lambda types:

kotlin
val composableType = LambdaTypeName.get(
  receiver = null,
  parameters = listOf(ParameterSpec.unnamed(ClassName("androidx.compose.ui", "Modifier"))),
  returnType = UNIT,
).annotated(AnnotationSpec.builder(ClassName("androidx.compose.runtime", "Composable")).build())

You can also pass annotations directly by class:

kotlin
val suppressedType = String::class.asTypeName().annotated(Suppress::class)

Or chain multiple annotations:

kotlin
val chainedAnnotations = String::class.asTypeName()
  .annotated(AnnotationSpec.builder(Suppress::class).addMember("%S", "unused").build())
  .annotated(Deprecated::class)

You can also update, replace, or clear annotations on a type with the copy() function:

kotlin
val unannotatedString = String::class.asTypeName()
  .annotated(Suppress::class)
  .copy(annotations = listOf())

Anonymous Inner Classes (`docs/anonymous-inner-classes.md`)

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.


Callable References (`docs/callable-references.md`)

Callable References

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.


Changelog (`docs/changelog.md`)

Change Log

Unreleased

  • New: Support for companion extensions. (#2406)
  • Fix: Carriage returns are no longer lost in %S and %P string literals. (#2380)
  • Fix: Class.asClassName() no longer drops the package slot of a class in the default package. (#2382)
  • Fix: A trailing newline in a %S or %P string argument that ends a CodeBlock is no longer trimmed or added. (#2391)

Version 2.4.0

Thanks to @eyupcanakman and @arimu1 for contributing to this release.

2026-09-07

  • New: Kotlin 2.4.20.
  • New: KSP 2.3.11.
  • New: Support for explicit backing fields. (#2325)
  • New: value class validations have been relaxed to support multi-field value classes. (#2329)
  • New: Extract CodeBlockHolder interface for constructs that can hold a CodeBlock body and their builders. (#2331)
  • New: Add CodeBlock.Builder.addComment() for adding // comments. (#2340)
  • New: Add CodeBlockHolder.Builder.addComment(). (#2347)
  • New: Use [] syntax instead of arrayOf when emitting annotation arguments. (#2361)
  • Fix: Keep the // prefix on wrapped file comment lines. (#2315)
  • Fix: TypeVariableName.equals/hashCode no longer overflow on recursively bound generics like Enum<E : Enum<E>>. (#2320)
  • Fix: get and set operator function names are no longer escaped with backticks. (#2338)
  • Fix: Don't special case varargs in KSAnnotation.toAnnotationSpec. (#2360)
  • Fix: KSType.toTypeName() with the default TypeParameterResolver.EMPTY now resolves type parameters from their enclosing declarations instead of throwing. (#2368)
  • Fix: Emit context parameters after annotations in FunSpec and PropertySpec. (#2374)
  • Fix: An expression body no longer leaks indentation into later declarations. (#2365)

Version 2.3.0

Thanks to @haruue, @hfhbd, @yz4230, @mina-jaff, @BoD,
@RaoPrashanth for contributing to this release.

2026-03-27

  • New: Kotlin 2.3.20.
  • New: KSP 2.3.6.
  • New: Support for defining type aliases inside other types. (#2167)
  • New: Add TypeName.annotated() convenience API for adding annotations to types. (#2281)
  • New: Add FileSpec.addImport(MemberName) to explicitly import a member. (#2198)
  • New: Add NameAllocator.contains to check if a given tag is already allocated. (#2160)
  • New: :interop:javapoet module has been discontinued and is not published starting with this release. (#2292)
  • New: In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. (#2135)
  • Fix: Annotation array parameters with annotation elements now correctly handled. (#2142)
  • Fix: KType.asTypeName now correctly handles recursively bound generics (e.g. T : Comparable<T>). (#2162)
  • Fix: Don't convert multi-statement function to expression body. (#2163)
  • Fix: FunSpec.beginControlFlow now accepts nullable arguments for consistency with CodeBlock.beginControlFlow. (#2174)
  • Fix: Ignore illegal characters inside escaped identifiers. (#2204)
  • Fix: emitNullable correctly when deferring type in CodeWriter. (#2217)
  • Fix: Escape /* and */ when emitting Kdoc. (#2258)
  • Fix: Generate imports for extension members used via %M in KDocs. (#2266)

Version 2.2.0

Thanks to @IRus for contributing to this release.

2025-05-16

  • New: Kotlin 2.1.21.
  • New: KSP 2.1.21-2.0.1.
  • New: Add support for context parameters. (#2112)
  • New: Eliminate Guava dependency. (#2110)
  • New: Migrate to Dokka plugin V2. (#2113)

Version 2.1.0

Thanks to @ForteScarlet, @TrevorSStone,
@RaoPrashanth, @damianw, @ansehoon1999,
@TheMrMilchmann for contributing to this release.

2025-02-25

  • New: Kotlin 2.1.10.
  • New: KSP 2.1.10-1.0.30.
  • Fix: Support delegates on anonymous classes. (#2034)
  • Fix: Prevent aliased imports from colliding with existing imports. (#2052)
  • Fix: TypeName.MUTABLE_MAP_ENTRY now correctly uses the MutableEntry nested class name. (#2061)
  • Fix: Use the same aliased import for both the nullable and non-nullable versions of a type. (#2068)
  • Fix: Allow zero methods in a functional interface if it has a superinterface. (#2075)

Version 2.0.0

Thanks to @brokenhappy, @tajobe, @niyajali,
@ForteScarlet for contributing to this release.

2024-10-23

This release is source- and binary-compatible with KotlinPoet 1.x.

The most important behavior change in this release is that spaces in generated code don't wrap by
default anymore.

KotlinPoet 1.x used to replace space characters with newline characters whenever a given line of
code exceeded the length limit. This usually led to better code formatting, but could also lead to
compilation errors in generated code. Non-breaking spaces could be marked by the ยท character, but
the discoverability of this feature wasn't great.

KotlinPoet 2.0 does not wrap spaces, even if the line of code they occur in exceeds the length
limit. The newly introduced โ™ข character can be used to mark spaces that are safe to wrap, which
can improve code formatting. The ยท character has been preserved for compatibility, but its
behavior is now equivalent to a regular space character.

  • New: Kotlin 2.0.10.
  • New: Spaces don't break by default.
  • New: New โ™ข placeholder representing a space that is safe to wrap.
  • New: Add KSTypeAlias.toClassName(). (#1956)
  • New: Add KSType.toClassNameOrNull(). (#1956)
  • Fix: Enum classes that only have an init block now also generate the required semicolon. (#1953)
  • Fix: Preserve typealiases in KSAnnotation.toAnnotationSpec(). (#1956)
  • Fix: Preserve nullability in KSType.toClassName(). (#1956)

Version 1.18.1

Thanks to @mitasov-ra for contributing to this release.

2024-07-15

  • Fix: Workaround for KT-18706: KotlinPoet now generates import aliases without backticks (#1920).

    kotlin
    // before, doesn't compile due to KT-18706
    import com.example.one.`$Foo` as `One$Foo`
    import com.example.two.`$Foo` as `Two$Foo`
    
    // now, compiles
    import com.example.one.`$Foo` as One__Foo
    import com.example.two.`$Foo` as Two__Foo

Version 1.18.0

Thanks to @DanielGronau for contributing to this release.

2024-07-05

  • New: Kotlin 2.0.0.
  • New: KSP 2.0.0-1.0.22.
  • New: Promote kotlinpoet-metadata out of preview to stable.
  • New: Migrate kotlinpoet-metadata to stable org.jetbrains.kotlin:kotlin-metadata-jvm artifact for Metadata parsing.
  • New: Make enum entry references in KSAnnotation.toAnnotationSpec() and KSClassDeclaration.toClassName() more robust.
  • Fix: Don't expand typealiases of function types to LambdaTypeNames in KSTypeReference.toTypeName().
  • Fix: Avoid rounding small double and float values in %L translation (#1927).
  • Fix: Fix typealias type argument resolution in KSP2 (#1929).

Version 1.17.0

Thanks to @jisungbin, @hfhbd, @evant, @sgjesse, @sebek64 for
contributing to this release.

2024-05-24

  • Change: kotlinx-metadata 0.9.0. Note that the KotlinClassMetadata.read is deprecated in 0.9.0 and replaced with
    readStrict (#1830).
    • Note: we now also provide lenient parameters to map to the underlying readStrict() and readLenient() calls
      (#1766).
    • We have also removed various Class/TypeElement/Metadata-to-KmClass APIs from the public API, as these are
      trivial to write now with kotlinx-metadata's newer APIs and allows us to focus the API surface area of this artifact
      better (#1891).
  • New: Supertype list wraps to one-per-line if the primary constructor spans multiple lines (#1866).
  • New: Extract MemberSpecHolder interface for constructs that can hold PropertySpecs and FunSpecs and their
    builders (#1877).
  • New: joinToCode variant which operates on any type, but requires a transform lambda to convert each element into a
    CodeBlock (#1874).
  • New: Support annotation type arguments in KSAnnotation.toAnnotationSpec() (#1889).
  • Fix: Prevent name clashes between a function in class and a function call in current scope (#1850).
  • Fix: Fix extension function imports (#1814).
  • Fix: Omit implicit modifiers on FileSpec.scriptBuilder (#1813).
  • Fix: Fix trailing newline in PropertySpec (#1827).
  • Fix: KSAnnotation.toAnnotationSpec writes varargs in place instead of making them an array to work around a Kotlin
    issue with OptIn annotations (#1833).
  • Fix: MemberNames without a package are now correctly imported (#1841)
  • Fix: Throw if primary constructor delegates to other constructors (#1859).
  • Fix: Aliased imports with nested class (#1876).
  • Fix: Check for error types in KSType.toClassName() (#1890).
  • Fix: Support generating a single import for overloaded MemberNames (#1909).

Version 1.16.0

Thanks to @drawers, @rickclephas for contributing to this release.

2024-01-18

  • New: Kotlin 1.9.22.
  • New: KSP 1.9.22-1.0.16.
  • New: Add NameAllocator API to control keyword pre-allocation (#1803).
  • Fix: Fix issue with missing suspend modifier in KSTypeReference.toTypeName (#1793).
  • Fix: Honour same-package import aliases (#1794).
  • Fix: Always include parameter docs in the type header (#1800).

Version 1.15.3

Thanks to @gabrielittner for contributing to this release.

2023-12-04

  • Fix: Fix nullability of lambdas in KSTypeReference.toTypeName (#1756).

Version 1.15.2

Thanks to @evant for contributing to this release.

2023-11-30

  • New: Kotlin 1.9.21.
  • New: KSP 1.9.21-1.0.15.
  • New: KSP: more accurately represent function types (#1742).

Version 1.15.1

2023-11-19

  • Fix: Fix a regression introduced by #1637, where a superfluous newline is added to a type's KDoc
    if it has a primary constructor with no docs (#1727).

Version 1.15.0

2023-11-18

Thanks to @drawers, @fejesjoco, @takahirom,
@martinbonnin, @mcarleio for contributing to this release.

In this release the :kotlinpoet module has been converted to a Kotlin Multiplatform module
(#1654), though for now it only supports the JVM target. Important: unless you're building
with Gradle, you will now need to depend on the kotlinpoet-jvm artifact, instead of kotlinpoet -
see Downloads for instructions.

  • New: Kotlin 1.9.20.
  • New: KSP 1.9.20-1.0.14.
  • New: Extract TypeSpecHolder interface for constructs that can hold a TypeSpec and their builders (#1723).
  • New: Expose relative path from FileSpec (#1720).
  • New: Return the generated path from FileSpec.writeTo(). (#1514).
  • New: Remove default compatibility from unstable types (#1662).
  • New: Deprecate TypeSpec.expectClassBuilder() and TypeSpec.valueClassBuilder() (#1589).
  • New: Add option to convert KSAnnotation to AnnotationSpec while omitting default values (#1538).
  • New: Add FileSpec.builder convenience for MemberName (#1585).
  • Fix: Set DecimalFormatSymbols.minusSign for consistency across locales (#1658).
  • Fix: Fix link to incremental KSP in KDoc (#1638).
  • Fix: Emit primary constructor KDoc (#1637).
  • Change: kotlinx-metadata 0.7.0. This is a breaking change for users of the :kotlinpoet-metadata
    module, as most Flags-API extensions have been removed in favor of the now-available first-party versions.

Version 1.14.2

2023-05-30

  • Fix: Fix one more missing API in binary compatibility override in Annotatable.Builder (#1581).

Version 1.14.1

2023-05-29

  • Fix: Restore ABI stability for annotatable and documentable builders (#1580).

Version 1.14.0

2023-05-29

Thanks to @Omico, @drawers, @RBusarow for contributing to this release.

  • New: Kotlin 1.8.21.

  • New: KSP 1.8.21-1.0.11.

  • New: Enable default methods in Java bytecode (#1561).

  • New: Group Kotlin and Renovate updates together in Renovate (#1562).

  • New: Extract trait interface for annotatable constructs and their builders (#1564).

  • New: Extract trait interface for documentable constructs and their builders (#1571).

  • New: Document the usage of STAR (#1572).

  • New: Add builder for FunSpec which accepts a MemberName (#1574).

  • Fix: Omit public modifier on override function or constructor parameters (#1550).

  • Fix: Correct handling of members in various types (#1558).

  • Fix: Function return types now default to Unit unless explicitly set (#1559).

    Previously, when FunSpec didn't have a return type specified and an expression body was produced, no return
    type would be emitted. However, starting from 1.14.0, KotlinPoet will not add Unit as a return type in such
    cases. In order to correct the generated output, you are to specify the actual return type of the FunSpec.

    Before 1.14.0, if omitted, no return type is produced:

    kotlin
    val funSpec = FunSpec.builder("foo")
      .addStatement("return 1")
      .build()
    kotlin
    public fun foo() = 1

    From 1.14.0, the return type defaults to Unit if not otherwise set:

    kotlin
    val funSpec = FunSpec.builder("foo")
      .addStatement("return 1")
      .build()
    kotlin
    public fun foo(): Unit = 1 // โŒ

    To fix it, explicitly define the return type:

    diff
    val funSpec = FunSpec.builder("foo")
    +  .returns(INT)
       .addStatement("return 1")
       .build()
    kotlin
    public fun foo(): Int = 1 // โœ…

    Additionally, as part of this change, FunSpec.returnType has changed to be non-nullable. This is a source- and
    binary-compatible change, although if you were performing null-checks then new warnings may appear after upgrade.

  • Fix: Append nested class names to alias during name lookup (#1568).

  • Fix: Allow PropertySpec with context receivers and without getter or setter (#1575).

Version 1.13.2

2023-05-05

Thanks to @Squiry for contributing to this release.

  • Fix: KSType.toTypeName fixed to work with aliased types (#1534).

Version 1.13.1

2023-04-28

Thanks to @rickclephas for contributing to this release.

  • Fix: Look at canonical names instead of just package names when generating import aliases (#1519).
  • Fix: Ignore KSP annotation arguments without a value (#1523).
  • Fix: Fix arguments handling in KSType.toTypeName() (#1529).

Version 1.13.0

2023-04-06

Thanks to @popematt, @bitPogo, @mars885, @sjudd, @Sironheart,
@polarene, @DeoTimeTheGithubUser, @drawers for contributing to this release.

  • New: Kotlin 1.8.0.
  • New: KSP 1.8.0-1.0.9.
  • New: Support context receivers on TypeSpecs + extract ContextReceivable API (#1269).
  • New: Optimize OriginatingElements and TagMap implementations (#1270).
  • New: Auto-generate import aliases for types and members (#1355).
  • New: Insert underscores into large decimal literals (#1384).
  • New: New factory function FileSpec.builder(ClassName) (#1397).
  • Fix: Fix StackOverflowError when calling KSTypeArgument.toTypeName() for a wildcard in a recursive type bound (#1272).
  • Fix: Fix transitive aliases (#1306).
  • Fix: Fix Aliases as TypeArgument (#1321).
  • Fix: Don't escape special characters inside raw strings (#1331).
  • Fix: Fix KSP interop's output of the annotation parameter value of type Char (#1338).
  • Fix: Fix KSP interop's output for primitive arrays (#1340).
  • Fix: Avoid emitting public if shouldEmitPublicModifier returns false (#1342).
  • Fix: Fix context receivers being rendered in an incorrect position when on a nullable/suspending LambdaTypeName (#1454).
  • Fix: Do not use bestGuess for KClass.asClassName (#1469).
  • Fix: Handle fake nested types with platform mapped parents (#1472).
  • Fix: Fix TypeName equals (#1477).
  • Fix: Make equals consistent with compareTo for ClassName (#1506).

Version 1.12.0

2022-06-13

Thanks to @WhosNickDoglio, @sullis, @DRSchlaubi,
@martinbonnin, @seriouslyhypersonic, @ephemient,
@dkilmer, @aksh1618, @zsqw123, @roihershberg for
contributing to this release.

  • New: Kotlin 1.7.0.
  • New: Add support for context receivers.
  • New: Add support for external property getter.
  • New: interop-ksp API promoted to stable.
  • Fix: Resolve enum constants when emitting types.
  • Fix: Fix type argument mapping when processing typealiases with KSP.
  • Fix: Properly unwrap KSTypeAlias with an unused type parameter.
  • Fix: Unwrap nested KSTypeAlias-es recursively.
  • Fix: Add support for context receivers @PropertySpec and fix issues with annotations.
  • Fix: Treat header and impl as keywords (workaround for KT-52315).
  • Fix: Use %N instead of %L for annotation arg names so keywords are handled.
  • Fix: Improve handling of long return expressions.

Version 1.11.0

2022-03-24

Thanks to @liujingxing and @BoD for contributing to this release.

  • New: Kotlin scripting support in FileSpec.
kotlin
val spec = FileSpec.scriptBuilder("Taco")
  .addStatement("println(%S)", "hello world!")
  .addKotlinDefaultImports()
  .build()

Generates a Taco.kts file with the following contents:

kotlin
println("hello world!")
  • New: Emit trailing commas for multi-line parameters and annotations.
  • New: Add KSAnnotation.toAnnotationSpec().
  • New: Add Unit and CharSequence conversions in javapoet-interop.
  • New: Add support for default imports in FileSpec.
    • This is particularly oriented at scripting support, but can also be used in non-script files.
  • New: Update to Kotlin 1.6.10.
  • Fix: Fail compilation if you only pass one string to ClassName.
  • Fix: Inline val property if its getter is inline.
  • Fix: Add yield to the list of reserved keywords.
  • Fix: Enforce only allowed parameter modifiers in ParameterSpec (i.e. crossinline, vararg, and noinline).
  • Fix: Fix CodeBlocks in class delegation getting toString()'d instead of participating in code writing.
  • Fix: Error when attempting to convert KSP error types (i.e. if KSType.isError is true) to TypeName.

Version 1.10.2

2021-10-22

Thanks to @glureau and @goooler for contributing to this release.

  • New: Switch AnnotationSpec.get() to use the arrayOf() syntax instead of [].
  • Fix: Don't wrap aliasing imports with long package names.
  • Fix: Don't wrap type names inside line comments.
  • Fix: Ignore Java's @Deprecated annotations on synthetic methods for annotations.

Version 1.10.1

2021-09-21

Thanks to @evant for contributing to this release.

  • Fix: Correct generation of typealiases with type args in KSP interop.
  • Fix: Add missing default TypeParameterResolver.EMPTY argument to
    fun KSTypeArgument.toTypeName in KSP interop.

Version 1.10.0

2021-09-20

Thanks to @martinbonnin, @idanakav, @goooler, and
@anandwana001 for contributing to this release.

  • New: Add a new KSP interop artifact. See docs for more details.
  • New: Add a new JavaPoet interop artifact. See docs for more
    details.
  • New: Allow copying a ParameterizedTypeName with new type arguments via new copy() overload.
  • kotlinx-metadata artifacts have been consolidated to a single com.squareup:kotlinpoet-metadata
    maven artifact. The previous kotlinpoet-metadata-* subartifacts are no longer published.
  • New: TypeNameAliasTag has been moved to KotlinPoet's main artifact under TypeAliasTag, for
    reuse with KSP interop.
  • ImmutableKm* classes have been removed. They were deemed to be a needless abstraction over the base kotlinx-metadata Km types. All usages of these should be substituted with their non-immutable base types.
  • Fix: Fix self-referencing type variables in metadata parsing.
  • Fix: Use delicate APIs rather than noisy logging ones when converting annotation mirrors in
    AnnotationSpec.get.
  • Fix: Update error message when metadata cannot be read to a more actionable one.
  • Fix: Avoid escaping already escaped strings.
  • Add docs about kotlin-reflect usage.
  • Avoid using kotlin-reflect for looking up Unit types where possible.
  • Test all the way up to JDK 17.
  • Update Kotlin to 1.5.31.

Version 1.9.0

2021-06-22

  • New: Kotlin 1.5.10.
  • New: Previously deprecated API to interop with Java reflection and Mirror API have been
    un-deprecated and marked with @DelicateKotlinPoetApi annotation.
  • New: CodeBlock.Builder.withIndent helper function.
  • New: Allow changing initializers and default values in ParameterSpec.Builder and
    PropertySpec.Builder after they were set.
  • New: MemberName.isExtension property that instructs KotlinPoet to always import the member,
    even if conflicting declarations are present in the same scope.
  • Fix: Escape member names that only contain underscores.
  • Fix: Always emit an empty primary constructor if it was set via TypeSpec.primaryConstructor.

Version 1.8.0

2021-03-29

  • New: Kotlin 1.4.31.
  • New: Add KModifier.VALUE to support value class declarations.
  • New: Allow using a custom ClassLoader with ReflectiveClassInspector.
  • New: Update to kotlinx-metadata 0.2.0.
  • Fix: Ensure ImmutableKmProperty.toMutable() copies fieldSignature.
  • Fix: Prevent name clashes between an imported MemberName and a member in current scope.
  • Fix: Prevent name clashes between a type and a supertype with the same name.
  • Fix: Don't generate empty body for expect and external functions.
  • Fix: Don't allow expect or external classes to initialize supertypes.
  • Fix: Disallow delegate constructor calls in external classes.
  • Fix: Allow non-public primary constructors inside inline/value classes.
  • Fix: Allow init blocks inside inline/value classes.
  • Fix: Omit redundant abstract modifiers on members inside interfaces

Version 1.7.2

2020-10-20

  • New: Detect expression bodies with returnยท and throwยท prefixes.
  • Fix: Omit visibility modifiers on custom accessors.

Version 1.7.1

2020-10-15

  • Fix: 1.7.0 was published using JDK 11 which set "org.gradle.jvm.version" to "11" in Gradle
    metadata, making it impossible to use the library on earlier Java versions (see
    #999). 1.7.1 is published with JDK 8, which fixes the problem.

Version 1.7.0

2020-10-14

  • New: Kotlin 1.4.10.
  • New: Generated code is now compatible with the explicit API mode by default.
  • New: Escape soft and modifier keywords, in addition to hard keywords.
  • New: Improve enum constants generation for cleaner diffs.
  • New: Disallow setters on immutable properties.
  • New: Ensure trailing new lines in expression bodies.
  • New: Ensure trailing new lines after parameterless custom setters.
  • Fix: Don't auto-convert properties with custom accessors to primary constructor properties.
  • Fix: Don't allow parameterless setters with body.
  • Fix: Prevent auto-wrapping spaces inside escaped keywords.

Version 1.6.0

2020-05-28

  • New: Deprecate Mirror API integrations.

    Mirror API integrations, such as TypeElement.asClassName() and
    FunSpec.overriding(ExecutableElement), are being deprecated in this release. These KotlinPoet
    APIs are most often used in annotation processors. Since kapt runs annotation processors over
    stubs, which are Java files, a lot of the Kotlin-specific information gets lost in translation
    and cannot be accessed by KotlinPoet through the Mirror API integrations. Examples include:

    • Alias types, such as kotlin.String, get converted to their JVM representations, such as
      java.lang.String.
    • Type nullability information is not accessible.
    • suspend functions are seen as simple functions with an additional Continuation parameter.

    The correct solution is to switch to KotlinPoet-metadata or
    KotlinPoet-metadata-specs API, which fetches Kotlin-specific
    information from the @Metadata annotation and produces correct KotlinPoet Specs. We may explore
    adding new metadata-based alternatives to the deprecated APIs in the future.

  • New: Kotlin 1.3.72.

  • New: Improve MemberName to support operator overloading.

  • New: Support generics in AnnotationSpec.

  • New: Add support for functional interfaces.

  • New: Make more FunSpec.Builder members public for easier mutation.

  • Fix: Properly propagate implicit type and function modifiers in nested declarations.

  • Fix: Properly escape type names containing $ character.

  • Fix: Don't emit LambdaTypeName annotations twice.

  • Fix: Preserve tags in TypeName.copy().

Version 1.5.0

2020-01-09

KotlinPoet now targets JDK8, which means that executing a build that includes KotlinPoet as a
dependency on a machine with an older version of JDK installed won't work. This has no effect on
the code that KotlinPoet produces
: the code can still be compiled against JDK6, as long as it
doesn't use any features that were introduced in newer releases.

  • New: Kotlin 1.3.61.
  • New: Add support for processing FileFacades in KotlinPoet-metadata.
  • New: Add support for inner nested and companion objects on annotation classes.
  • New: Improve error messages for mismatched open/close statement characters.
  • New: Tag AnnotationSpecs with the annotation mirror when available.
  • New: Include annotations on enum entries when creating TypeSpecs from metadata.
  • Fix: Fix metadata parsing for types.
  • Fix: Allow file names that are Kotlin keywords.
  • Fix: Properly escape type alias names with backticks.
  • Fix: Allow creating TypeSpecs with names that can be escaped with backticks.
  • Fix: Properly escape enum constant names with backticks.
  • Fix: Maintain proper ordering of properties and initializers when emitting a TypeSpec.
    Note: with this change, any properties declared after any initializer blocks will not be
    added to the primary constructor and will instead be emitted inside the TypeSpec body.
  • Fix: Don't emit a leading new line if type KDoc is empty but parameter KDocs are present.
  • Fix: Ensure KotlinPoet-metadata resolves package names properly.

Version 1.4.4

2019-11-16

  • Fix: Support reified inline types in KotlinPoet-metadata.

Version 1.4.3

2019-10-30

  • Fix: Don't emit stubs for abstract functions in KotlinPoet-metadata.

Version 1.4.2

2019-10-28

  • Fix: Properly handle abstract elements in KotlinPoet-metadata.
  • Fix: Properly handle typealiases in KotlinPoet-metadata.
  • Fix: Properly render % symbols at the end of KDocs.

Version 1.4.1

2019-10-18

  • New: Add annotations support to TypeAliasSpec.
  • New: Read type annotations from Kotlin Metadata.
  • New: Introduce ImmutableKmDeclarationContainer.
  • Fix: Use full package name for shading auto-common.
  • Fix: Support reading self-type variables (e.g. Asset<A : Asset<A>>) from Kotlin Metadata.

Version 1.4.0

2019-09-24

  • New: This release introduces the new KotlinPoet-metadata API that makes it easy to introspect
    Kotlin types and build KotlinPoet Specs based on that data.

    The strategy for type introspection is driven by ClassInspector, which is a basic interface for
    looking up JVM information about a given Class. This optionally is used by the
    toTypeSpec()/toFileSpec() APIs in kotlinpoet-metadata-specs artifact to inform about
    Classes with information that isnโ€™t present in metadata (overrides, JVM modifiers, etc). There
    are two batteries-included implementations available in ReflectiveClassInspector
    (for reflection) and ElementsClassInspector (for the javax Elements API in annotation
    processing). These implementations are available through their respective
    kotlinpoet-classinspector-* artifacts. For more information refer to the
    KotlinPoet-metadata-specs README.

    At the time of this release the API is in experimental mode and has to be opted into via the
    KotlinPoetMetadataPreview annotation.

  • New: Kotlin 1.3.50.

  • New: A new constructor to simplify creation of ParameterSpec instances.

  • New: New ClassName constructors.

  • New: TypeName and subclasses can now store tags.

  • New: Optional parameters added to toBuilder() methods of most Specs.

  • New: List overrides for Spec methods that accept varargs.

  • New: CodeBlock.Builder.clear() helper method.

  • New: FunSpec.Builder.clearBody() helper method.

  • Fix: Properly escape enum constant names.

  • Fix: Ensure trailing newlines in KDoc and function bodies.

  • Fix: TypeVariableNames with empty bounds will now default to Any?.

  • Fix: Don't emit parens for primary constructors.

  • Fix: ClassNames with empty simple names are not allowed anymore.

  • Fix: Throw if names contain illegal characters that can't be escaped with backticks.

Version 1.3.0

2019-05-30

  • New: Don't inline annotations in the primary constructor.
  • New: Force new lines when emitting primary constructors.
  • New: Support using MemberNames as arguments to %N.
  • New: Add more ClassName constants: ClassName.STRING, ClassName.LIST, etc.
  • New: Add ClassName.constructorReference() and MemberName.reference().
  • New: Make %N accept MemberNames.
  • New: Escape spaces in import aliases.
  • New: Escape spaces in ClassNames.
  • New: Escape spaces in MemberNames.
  • New: Escape imports containing spaces.
  • New: Escape package name containing spaces.
  • New: Use 2-space indents.
  • New: Only indent one level on annotation values.
  • Fix: Pass only unique originating elements to Filer.
  • Fix: Fix bug with MemberNames in same package nested inside a class.

Version 1.2.0

2019-03-28

  • New: Add writeTo(Filer) and originating element API.
  • New: Make *Spec types taggable.
  • New: Make FunSpec.Builder#addCode take vararg Any?.
  • Fix: Import members from default package.
  • Fix: Add non-wrapping spaces in control flow creation methods.
  • Fix: Named "value" argument being omitted in annotation array types.

Version 1.1.0

2019-02-28

  • New: Kotlin 1.3.21.
  • New: Support referencing members using %M and MemberName type.
  • New: Add extensions for getting a MemberName from a ClassName, KClass and Class.
  • New: Allow passing CodeBlocks as arguments to %P.
  • New: Allow interface delegation for objects.
  • Fix: Don't emit visible whitespace in toString().
  • Fix: Prevent line wrapping in weird places inside function signature.
  • Fix: No line wrapping between val and property name.
  • Fix: Allow passing line prefix into LineWrapper to enable proper line wrapping in KDoc.
  • Fix: Add newline for TypeSpec Kdoc with no tags.
  • Fix: Add newline for remaining Specs.
  • Fix: Fix kdoc formatting for property getter/setters.
  • Fix: Don't wrap single line comments inside FunSpec.
  • Fix: Add non-wrapping package name.
  • Fix: Remove n^2 algorithm in CodeWriter.resolve() by precomputing all of the nested simple names of a TypeSpec.
  • Fix: Fix edge case with empty enum classes.
  • Fix: Fix Nullable Type Parameter handling in KType.asTypeName().
  • Fix: Fix incorrect long comment wrapping in FileSpec.
  • Fix: Attach primary constructor param/property KDoc to the element vs emitting it inside the type header.

Version 1.0.1

2019-01-02

  • New: Allow enums without constants.
  • New: Improved formatting of TypeSpec KDoc.
  • New: Support @property and @param KDoc tags in TypeSpec.
  • Fix: Use pre-formatted strings for arguments to %P.

Version 1.0.0

2018-12-10

  • New: Kotlin 1.3.11.
  • Fix: Prevent wrapping in import statements.

Version 1.0.0-RC3

2018-11-28

  • New: Kotlin 1.3.10.
  • New: Add %P placeholder for string templates.
  • New: Add support for receiver kdoc.
  • New: Avoid emitting Unit as return type.
  • New: Add support for empty setters.
  • New: Add checks for inline classes.
  • New: Escape property and variable names if keywords.
  • New: Replace %>, %<, %[, %] placeholders with โ‡ฅ, โ‡ค, ยซ, ยป.
  • New: Replace %W with space, and add ยท as a non-breaking space.
  • New: Change TypeName to sealed class.
  • New: Documentation improvements.
  • New: Replace TypeName modifier methods with copy().
  • New: Rename members of WildcardTypeName to match with the producer/consumer generics model.
  • New: Rename TypeName.nullable into TypeName.isNullable.
  • New: Rename LambdaTypeName.suspending into LambdaTypeName.isSuspending.
  • New: Rename TypeVariableName.reified into TypeVariableName.isReified.
  • Fix: Emit star-projection only for types with Any? upper bound.
  • Fix: Fold property with escaped name.

Version 1.0.0-RC2

2018-10-22

  • New: Kotlin 1.2.71.
  • New: README improvements.
  • New: Allow opening braces and params in beginControlFlow().
  • New: Add KDoc to ParameterSpec, collapse into parent KDoc.
  • New: Support TypeVariables in PropertySpec.
  • New: Add parens for annotated types in LambdaTypeName.
  • New: Improve error messaging and documentation for inline properties.
  • New: Allow sealed classes to declare abstract properties.
  • New: Added buildCodeBlock() helper function.
  • New: Allow using CodeBlocks with statements as property initializers and default parameter values.
  • New: Rename NameAllocator.clone() into `NameAllocator.copy().
  • New: Rename TypeName.asNonNullable() to TypeName.asNonNull().
  • New: Remove PropertySpec.varBuilder() (use mutable() instead).
  • New: Allow importing top-level members in default package.
  • New: Add overloads to add KDoc to return type.
  • Fix: Distinguishing IntArray and Array when creating TypeName.
  • Fix: Use TypeName instead of ClassName as parameter type of plusParameter().
  • Fix: Keep type-parameter variance when constructing TypeName from KType.
  • Fix: Don't validate modifiers when merging properties with primary constructor parameters.
  • Fix: Escape $ characters in formatted strings.
  • Fix: FileSpec.Builder blank package and subfolder fix.
  • Fix: Append new line at end of parameter KDoc.
  • Fix: Add parameter KDoc in toBuilder().

Version 1.0.0-RC1

2018-07-16

  • New: Escape keywords in imports and canonical class names.
  • New: Improve external support.
  • New: Extensions for KType and KTypeParameter.
  • New: Add builder methods to simplify adding common kotlin.jvm annotations.
  • New: Enums are able to have companion objects.
  • New: Add missing primaryConstructor & companionObject to TypeSpec#toBuilder().
  • New: Make subtype checking vals inside Kind public.
  • New: Escape (class/property/function/variable) names automatically if they contain space, hyphen, or other symbols.
  • New: Improve ParameterizedTypeName API.
  • New: Add WildcardTypeName.STAR constant.
  • New: Expose mutable builder properties and move their validations to build-time.
  • Fix: Use regular indents for parameter lists.
  • Fix: Inline annotations on properties defined in primary constructor.
  • Fix: Use Any? as the default type variable bounds.
  • Fix: Fix importing annotated TypeName.
  • Fix: If any primary constructor property has KDoc, put properties on new lines.
  • Fix: Properly emit where block in type signature.
  • Fix: Avoid type name collisions in primary constructor.
  • Fix: Remove implicit TypeVariable bound when more bounds are added.
  • Fix: Combine annotations and modifiers from constructor params and properties.
  • Fix: Replace delegate constructor args along with the constructor.

Version 0.7.0

2018-02-16

  • New: Increase indent to 4 spaces.
  • New: Delegate super interfaces as constructor parameters.
  • New: Support PropertySpecs as CodeBlock literals.
  • New: Support KDoc for TypeAliasSpec.
  • New: Allow for adding an initializer block inside a companion object.
  • New: Escape name in ParameterSpec which is also a keyword.
  • New: Escape names in statements.
  • New: Set com.squareup.kotlinpoet as automatic module name.
  • New: Support suspending lambda types.
  • New: Support named LambdaTypeName parameters.
  • New: Support dynamic type.
  • New: Disallow wildcard imports.
  • New: Depend on Kotlin 1.2.21.
  • Fix: Correct handling of super-classes/interfaces on anonymous classes.
  • Fix: Fix boundary filtering to Any?.
  • Fix: Wrap long property initializers.
  • Fix: Fix formatting and indentation of parameter lists.

Version 0.6.0

2017-11-03

  • New: Support lambda extensions.
  • New: Support renames in imports like import bar.Bar as bBar.
  • New: Support extension and inline properties.
  • New: Support reified types.
  • New: Expose enclosed types inside LambdaTypeName.
  • New: Depend on Kotlin Kotlin 1.1.51.
  • New: Improved API and formatting of annotations.
  • New: Improved multiplatform support.
  • Fix: Escape function and package names if they are a Kotlin keyword.
  • Fix: Properly format WildcardTypeName's class declaration.

Version 0.5.0

2017-09-13

  • New: Rename addFun() to addFunction().
  • New: Rename KotlinFile to FileSpec.
  • New: Rename KotlinFile.addFileAnnotation() to addAnnotation().
  • New: Rename KotlinFile.addFileComment() to addComment().
  • New: Support cross-platform code, including HEADER and IMPL modifiers.
  • New: Support type variables for type aliases.
  • New: Support constructor delegation.
  • New: Support named companion objects.
  • New: Depend on Kotlin 1.1.4-3.
  • Fix: Format one parameter per line when there are more than two parameters.
  • Fix: Don't emit braces when the constructor body is empty.
  • Fix: Do not invoke superclass constructor when no primary constructor.
  • Fix: Enforce the right modifiers on functions.

Version 0.4.0

2017-08-08

  • New: Change KotlinPoet's extensions like asClassName() to be top-level functions.
  • New: Add declaration-site variance support.
  • New: Improve handling of single expression bodies.
  • New: Support file annotations.
  • New: Support imports from the top-level file.
  • New: Accept superclass constructor parameters.
  • New: Support primary constructors using the constructor keyword.
  • Fix: Don't emit setter parameter types.
  • Fix: Support Kotlin keywords in NameAllocator.
  • Fix: Emit the right default parameters for primary constructors.
  • Fix: Format annotations properly when used as parameters.
  • Fix: Recognize imports when emitting nullable types.
  • Fix: Call through to the superclass constructor when superclass has a no-args constructor.
  • Fix: Omit class braces if all properties are declared in primary constructor.
  • Fix: Don't emit empty class bodies.
  • Fix: Emit the right syntax for declaring multiple generic type constraints.
  • Fix: Support properties on objects, companions and interfaces.
  • Fix: Use AnnotationSpec for throws.

Version 0.3.0

2017-06-11

  • New: Objects and companion objects.
  • New: TypeAliasSpec to create type aliases.
  • New: LambdaTypeName to create lambda types.
  • New: Collapse property declarations into constructor params.
  • New: Extension and invoke functions for creating type names: Runnable::class.asClassName().
  • New: Basic support for expression bodies.
  • New: Basic support for custom accessors.
  • New: Remove Filer writing and originating elements concept. These stem from javac annotation
    processors.
  • Fix: Generate valid annotation classes.
  • Fix: Use KModifier for varargs.
  • Fix: Use ParameterizedTypeName for array types.
  • Fix: Extract Kotlin name from KClass instead of Java name.
  • Fix: Emit valid class literals: Double::class instead of Double.class.
  • Fix: Emit modifiers in the expected order.
  • Fix: Emit the correct syntax for enum classes and overridden members.

Version 0.2.0

2017-05-21

  • New: Flip API signatures to be (name, type) instead of (type, name).
  • New: Support for nullable types.
  • New: Support delegated properties.
  • New: Extension functions.
  • New: Support top-level properties.
  • Fix: Inheritance should use : instead of extends and implements.
  • Fix: Make initializerBlock emit init {}.

Version 0.1.0

2017-05-16

  • Initial public release.

Code Block Format Strings (`docs/code-block-format-strings.md`)

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)

Code Control Flow (`docs/code-control-flow.md`)

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.


Constructors (`docs/constructors.md`)

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.


Context Parameters (`docs/context-parameters.md`)

Context Parameters

KotlinPoet supports Kotlin's context parameters feature, which allows you to add context parameters to functions and properties. Context parameters are added using the contextParameter() method:

kotlin
val greet = FunSpec.builder("greet")
  .contextParameter("user", String::class)
  .addStatement("println(\"Hello, ${'$'}user!\")")
  .build()

The code above generates:

kotlin
context(user: kotlin.String)
public fun greet() {
  println("Hello, $user!")
}

You can add multiple context parameters:

kotlin
val loggerType = ClassName("java.util.logging", "Logger")
val configType = ClassName("com.example", "Config")

val logger = ContextParameter("logger", loggerType)
val config = ContextParameter("config", configType)

val processData = FunSpec.builder("processData")
  .contextParameter(logger)
  .contextParameter(config)
  .addStatement("%N.info(\"Processing with config: ${'$'}%N\")", logger, config)
  .build()

This generates:

kotlin
context(logger: com.example.Logger, config: com.example.Config)
public fun processData() {
  logger.info("Processing with config: $config")
}

Note: context receivers and context parameters cannot be used together.


Contributing (`docs/contributing.md`)

Contributing

If you would like to contribute code you can do so through GitHub by forking
the repository and sending a pull request.

When submitting code, please make every effort to follow existing conventions
and style in order to keep the code as readable as possible. Please also make
sure your code compiles by running ./gradlew clean build.

When creating a pull request, please add a row in the changelog with the
patch description and PR # to Unreleased section.

Before your code can be accepted into the project you must also sign the
Individual Contributor License Agreement (CLA).

When submitting a pull request, please do not delete the default contents of
the pull request template (see pull_request_template.md) and make sure
you've completed the checklist before submitting. Pull requests that are
missing the checklist will be closed.


Enums (`docs/enums.md`)

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");
}

Functions (`docs/functions.md`)

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()

Wrapping is explicit

In order to guarantee code correctness, starting in version 2.0, KotlinPoet will never replace
spaces found in blocks of code with new line symbols, even 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()

The function will always be printed out like this:

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

While the output is correct, the resulting line of code is quite long and hard to read. KotlinPoet
is unable to understand the context of the expression and adjust the formatting for you, but
there's a trick you can use to declare a breaking space - use the โ™ข symbol where you know it's
safe to optionally wrap the line. 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
public fun foo(): Unit = (100..10000).map { number -> number * number }.map { number ->
  number.toString() }.also { string -> println(string) }

This is slightly better, but far from perfect - feel free to play around with other formatting
modifiers, such as the standard \n character which KotlinPoet honors, or the indentation
formatters (โ‡ฅ and โ‡ค) that the library ships with (see the documentation for CodeBlock for
more information).

Lastly, imperfect formatting is a known limitation of the library, as KotlinPoet by design
prioritizes correctness of generated code over style. If correct and clean formatting is important
for your use case, please consider post-processing KotlinPoet's output using a dedicated code
formatter.


Interfaces (`docs/interfaces.md`)

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()
}

Interop Kotlin Metadata (`docs/interop-kotlin-metadata.md`)

KotlinPoet-metadata

interop:kotlin-metadata is an API for working with Kotlin @Metadata annotations. Its API
sits atop kotlin-metadata,
offering extensions for its types + JVM metadata information. This can be used to read
Kotlin language semantics off of Class or TypeElement @Metadata annotations.

Example

kotlin
data class Taco(val seasoning: String, val soft: Boolean) {
  fun prepare() {

  }
}

val kmClass = Taco::class.toKmClass()

// Now you can access misc information about Taco from a Kotlin lens
println(kmClass.name)
kmClass.properties.forEach { println(it.name) }
kmClass.functions.forEach { println(it.name) }

Flags

There are a number of boolean flags available to types as well under Flags.kt. These read the
underlying kotlin-metadata Flags property.

Using the Taco example above, we can glean certain information:

kotlin
println("Is class? ${kmClass.isClass}")
println("Is data class? ${kmClass.isData}")

Interop with KotlinPoet

interop:kotlin-metadata offers an API for converting core kotlin-metadata Km types to
KotlinPoet source representations of their APIs. This includes full type resolution, signatures,
enclosed elements, and general stub source representations of the underlying API.

Example

kotlin
data class Taco(val seasoning: String, val soft: Boolean) {
  fun prepare() {
  }
}

val typeSpec = Taco::class.toTypeSpec()

// Or FileSpec
val fileSpec = Taco::class.toFileSpec()

Source representation

The generated representations are a best effort representation of the underlying source code.
This means that synthetic elements will be excluded from generation. Kotlin-specific language
features like lambdas or delegation will be coerced to their idiomatic source form.

To aid with this, toTypeSpec() and toFileSpec() accept optional ClassInspector instances
to assist in parsing/understanding the underlying JVM code. This is important for things like
annotations, companion objects, certain JVM modifiers, overrides, and more. While it is optional,
represented sources can be incomplete without this information available. Reflective and javax
Elements implementations are available under the
com.squareup.kotlinpoet.metadata.classinspectors package.

Generated sources are solely stub implementations, meaning implementation details of elements
like functions, property getters, and delegated properties are simply stubbed with TODO()
placeholders.

Known limitations

  • Only KotlinClassMetadata.Class and KotlinClassMetadata.FileFacade are supported for now. No support for SyntheticClass, MultiFileClassFacade, or MultiFileClassPart
  • @JvmOverloads annotations are only supported with ElementsClassInspector and not reflection.
  • Non-const literal values are only supported with ElementsClassInspector and not reflection.
  • ClassInspector data sourced from synthetic constructs are only supported with
    ReflectiveClassInspector and not elements. This is because the javax Elements API does not model
    synthetic constructs. This can yield some missing information, like static companion object properties
    or property: site target annotations.
  • Annotations annotated with AnnotationRetention.SOURCE are not parsable in reflection nor javax elements.

Interop Ksp (`docs/interop-ksp.md`)

KSP Extensions for KotlinPoet

interop:ksp is an interop API for converting
Kotlin Symbol Processing (KSP) types to KotlinPoet types and
writing to KSP CodeGenerator.

kotlin
dependencies {
  implementation("com.squareup:kotlinpoet-ksp:<version>")
}

Examples

Examples are based on reading the following property as a KSProperty:

kotlin
class Taco {
  internal inline val seasoning: String get() = "spicy"
}

Convert a KSType to a TypeName

kotlin
// returns a `ClassName` of value `kotlin.String`
seasoningKsProperty.type.toTypeName()

Convert a Modifier to a KModifier

kotlin
// returns `[KModifier.INLINE]`
seasoningKsProperty.modifiers.mapNotNull { it.toKModifier() }

Convert a Visibility to a KModifier

kotlin
// returns `KModifier.INTERNAL`
seasoningKsProperty.getVisibility().toKModifier()

Write to CodeGenerator

To write a FileSpec to a KSP CodeGenerator, simply call the FileSpec.writeTo(CodeGenerator, ...)
extension function.

kotlin
fileSpec.writeTo(codeGenerator)

Type Parameters

Type parameters can be declared on classes, functions, and typealiases. These parameters are then
available to all of its enclosed elements. In order for these elements to resolve these in KSP, you
must be able to reference these type parameters by their index.

In kotlinpoet-ksp this is orchestrated by the TypeParameterResolver API, which can be passed
into most toTypeName() (or similar) functions to give them access to enclosing type parameters
that they may reference.

The canonical way to create an instance of this is to call toTypeParameterResolver() on a
List<KSTypeParameter>.

Consider the following class and function

kotlin
abstract class Taco<T> {
  abstract val seasoning: T
}

To properly resolve the type of seasoning, we need to pass the class TypeParameterResolver to
toTypeName() so that it can properly resolve it.

kotlin
val classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
// returns `T`
val seasoningType = seasoningKsProperty.type.toTypeName(classTypeParams)

TypeParameterResolver is also composable to allow for multi-level nesting. toTypeParameterResolver()
has an optional parent parameter to provide a parent instance.

Consider our previous example again, but this time with a function that defines its own type parameters.

kotlin
class Taco<T> {
  fun <E> getShellOfType(param1: E, param2: T) {

  }
}

To resolve its parameters, we need to create a TypeParameterResolver from the function's
typeParameters and compose it with the enclosing class's type parameters as a parent.

kotlin
val classTypeParams = ksClassDeclaration.typeParameters.toTypeParameterResolver()
val functionTypeParams = ksFunction.typeParameters.toTypeParameterResolver(parent = classTypeParams)
// returns `[E, T]`
val seasoningType = ksFunction.parameterTypes.map { it.toTypeName(functionTypeParams) }

Incremental Processing

KSP supports incremental processing as
long as symbol processors properly indicate originating files in generated new files and whether or
not they are aggregating. kotlinpoet-ksp supports this via OriginatingKSFiles, which is a simple
API that sits atop KotlinPoet's Taggable API. To use this, simply add relevant originating files to
any TypeSpec, TypeAliasSpec, PropertySpec, or FunSpec builders.

kotlin
val functionBuilder = FunSpec.builder("sayHello")
  .addOriginatingKSFile(sourceKsFile)
  .build()

Like KotlinPoet's originating elements support for javac annotation processors, calling the
FileSpec.writeTo(CodeGenerator, ...) function will automatically collect and de-dupe these originating
KSFile references and automatically assemble them in the underlying Dependencies for KSP's reference.

Optionally you can define your own collection of files and pass them to the writeTo function, but usually
you don't need to do this manually.

Lastly - FileSpec.writeTo(CodeGenerator, ...) also requires you to specify if your processor is
aggregating or not via required parameter by the same name.

TypeAlias Handling

For typealias types, KSP interop will store a TypeAliasTag in the TypeName's tags with a reference to the abbreviated type. This can be useful for APIs that want to resolve all un-aliased types.


Kotlin Reflect (`docs/kotlin-reflect.md`)

kotlin-reflect

To generate source code from any KType, including information that's not accessible to
the builtin reflection APIs, KotlinPoet depends on kotlin-reflect. 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 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() and
typeNameOf<T>(). 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))

L For Literals (`docs/l-for-literals.md`)

%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(). 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.


M For Members (`docs/m-for-members.md`)

%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 (`docs/n-for-names.md`)

%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)
}

Objects (`docs/objects.md`)

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.


P For String Templates (`docs/p-for-string-templates.md`)

%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 CodeBlocks 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()}""")
}

Parameters (`docs/parameters.md`)

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 (`docs/properties.md`)

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()-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:

text
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.


S For Strings (`docs/s-for-strings.md`)

%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"
}

T For Types (`docs/t-for-types.md`)

%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.

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
}

Annotated Types

KotlinPoet supports annotated types. To add an annotation to a type, use the annotated() API:

kotlin
val annotatedType = String::class.asTypeName()
  .annotated(AnnotationSpec.builder(MyAnnotation::class).build())

For comprehensive examples of annotating types, see Annotations.


Type Aliases (`docs/type-aliases.md`)

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

Type aliases can also be added to types (classes, interfaces, objects) as well:

Note: Nested type aliases are a beta feature in Kotlin. See the official documentation
here for more details.

kotlin
val taco = TypeSpec.classBuilder("Taco")
  .addTypeAlias(TypeAliasSpec.builder("Topping", String::class).build())
  .build()

Which generates the following:

kotlin
package com.squareup.tacos

import kotlin.String

public class Taco {
  public typealias Topping = String
}

--- METRICS ---