xref: /aosp_15_r20/external/kotlinpoet/docs/functions.md (revision 3c321d951dd070fb96f8ba59e952ffc3131379a0)
1Functions
2=========
3
4All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
5body. This is only legal if it is enclosed by an abstract class or an interface.
6
7```kotlin
8val flux = FunSpec.builder("flux")
9  .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
10  .build()
11
12val helloWorld = TypeSpec.classBuilder("HelloWorld")
13  .addModifiers(KModifier.ABSTRACT)
14  .addFunction(flux)
15  .build()
16```
17
18Which generates this:
19
20```kotlin
21abstract class HelloWorld {
22  protected abstract fun flux()
23}
24```
25
26The other modifiers work where permitted.
27
28Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
29type for extension functions. All of these are configured with `FunSpec.Builder`.
30
31## Extension functions
32
33Extension functions can be generated by specifying a `receiver`.
34
35```kotlin
36val square = FunSpec.builder("square")
37  .receiver(Int::class)
38  .returns(Int::class)
39  .addStatement("var s = this * this")
40  .addStatement("return s")
41  .build()
42```
43
44Which outputs:
45
46```kotlin
47fun Int.square(): Int {
48  val s = this * this
49  return s
50}
51```
52
53## Single-expression functions
54
55KotlinPoet can recognize single-expression functions and print them out properly. It treats
56each function with a body that starts with `return` as a single-expression function:
57
58```kotlin
59val abs = FunSpec.builder("abs")
60  .addParameter("x", Int::class)
61  .returns(Int::class)
62  .addStatement("return if (x < 0) -x else x")
63  .build()
64```
65
66Which outputs:
67
68```kotlin
69fun abs(x: Int): Int = if (x < 0) -x else x
70```
71
72## Default function arguments
73
74Consider the example below.
75Function argument `b` has a default value of 0 to avoid overloading this function.
76
77```kotlin
78fun add(a: Int, b: Int = 0) {
79  print("a + b = ${a + b}")
80}
81```
82
83Use the `defaultValue()` builder function to declare default value for a function argument.
84
85```kotlin
86FunSpec.builder("add")
87  .addParameter("a", Int::class)
88  .addParameter(
89    ParameterSpec.builder("b", Int::class)
90      .defaultValue("%L", 0)
91      .build()
92  )
93  .addStatement("print(\"a + b = ${a + b}\")")
94  .build()
95```
96
97## Spaces wrap by default!
98
99In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
100with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
101function for example:
102
103```kotlin
104val funSpec = FunSpec.builder("foo")
105  .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
106  .build()
107```
108
109Depending on where it's found in the file, it may end up being printed out like this:
110
111```kotlin
112fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
113{ string -> println(string) }
114```
115
116Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
117KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
118there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
119otherwise use a space. Let's apply this to our example:
120
121```kotlin
122val funSpec = FunSpec.builder("foo")
123  .addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
124  .build()
125```
126
127This will now produce the following result:
128
129```kotlin
130fun foo() = (100..10000).map { number -> number * number }.map { number ->
131  number.toString()
132}.also { string -> println(string) }
133```
134
135The code is now correct and will compile properly. It still doesn't look perfect - you can play with
136replacing other spaces in the code block with `·` symbols to achieve better formatting.
137
138Another common use case where you'd want to ensure spaces don't wrap is when emitting string
139literals:
140
141```kotlin
142CodeBlock.of("""println("Class: $className")""")
143```
144
145If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
146
147```kotlin
148println("Class:
149very.long.class.name.Here")
150```
151
152KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space
153inside of it should never be wrapped. To make sure this case is handled correctly, use the `%S`
154modifier (as described in [%S for Strings](s-for-strings.md)):
155
156```kotlin
157CodeBlock.of("""println(%S)""", "Class: $className")
158```
159
160Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
161