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