-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split KotlinPoet docs into multiple pages
- Loading branch information
Showing
23 changed files
with
1,503 additions
and
1,475 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ classes | |
|
||
# Mkdocs files | ||
docs/1.x/* | ||
site | ||
|
||
obj | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
Callable References | ||
=================== | ||
|
||
[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](m-for-members.md). | ||
|
||
[callable-references]: https://kotlinlang.org/docs/reference/reflection.html#callable-references |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
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) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
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. |
Oops, something went wrong.