0
# Code Generation and Templates
1
2
Template-based code generation with placeholder substitution, supporting complex code structures and maintaining proper formatting and imports.
3
4
## Capabilities
5
6
### Code Block Generation
7
8
Creates formatted code fragments with placeholder substitution and proper indentation, forming the building blocks for all code generation in KotlinPoet.
9
10
```kotlin { .api }
11
/**
12
* A fragment of code with placeholder substitution
13
*/
14
class CodeBlock private constructor() {
15
/** Whether this code block is empty */
16
val isEmpty: Boolean
17
18
/** Whether this code block is not empty */
19
val isNotEmpty: Boolean
20
21
companion object {
22
/** Creates a code block from a format string and arguments */
23
fun of(format: String, vararg args: Any?): CodeBlock
24
25
/** Creates an empty code block */
26
fun builder(): Builder
27
28
/** Joins multiple code blocks with a separator */
29
fun join(codeBlocks: Iterable<CodeBlock>, separator: String = ", "): CodeBlock
30
fun join(codeBlocks: Iterable<CodeBlock>, separator: CodeBlock): CodeBlock
31
32
/** Creates a code block that joins with newlines */
33
fun joining(separator: String = ", "): Collector<CodeBlock, *, CodeBlock>
34
fun joining(separator: CodeBlock): Collector<CodeBlock, *, CodeBlock>
35
}
36
37
/** Builder for constructing CodeBlock instances */
38
class Builder {
39
/** Adds formatted code to the block */
40
fun add(format: String, vararg args: Any?): Builder
41
42
/** Adds named code with a parameter map */
43
fun addNamed(format: String, args: Map<String, *>): Builder
44
45
/** Adds a code statement (automatically adds newline) */
46
fun addStatement(format: String, vararg args: Any?): Builder
47
48
/** Adds raw code without formatting */
49
fun add(codeBlock: CodeBlock): Builder
50
51
/** Begins a control flow block (if, for, while, etc.) */
52
fun beginControlFlow(controlFlow: String, vararg args: Any?): Builder
53
54
/** Adds to the current control flow */
55
fun nextControlFlow(controlFlow: String, vararg args: Any?): Builder
56
57
/** Ends the current control flow block */
58
fun endControlFlow(): Builder
59
fun endControlFlow(controlFlow: String, vararg args: Any?): Builder
60
61
/** Increases indentation level */
62
fun indent(): Builder
63
64
/** Decreases indentation level */
65
fun unindent(): Builder
66
67
/** Clears all content from the builder */
68
fun clear(): Builder
69
70
/** Builds the CodeBlock */
71
fun build(): CodeBlock
72
}
73
}
74
```
75
76
**Usage Examples:**
77
78
```kotlin
79
import com.squareup.kotlinpoet.*
80
81
// Simple code block with placeholders
82
val simpleCode = CodeBlock.of("println(%S)", "Hello, World!")
83
84
// Code block with multiple statements
85
val multiStatementCode = CodeBlock.builder()
86
.addStatement("val name = %S", "Alice")
87
.addStatement("val age = %L", 25)
88
.addStatement("println(%S + name + %S + age)", "Name: ", ", Age: ")
89
.build()
90
91
// Control flow structures
92
val ifElseCode = CodeBlock.builder()
93
.beginControlFlow("if (age >= 18)")
94
.addStatement("println(%S)", "Adult")
95
.nextControlFlow("else")
96
.addStatement("println(%S)", "Minor")
97
.endControlFlow()
98
.build()
99
100
// For loop
101
val forLoopCode = CodeBlock.builder()
102
.beginControlFlow("for (i in 1..10)")
103
.addStatement("println(i)")
104
.endControlFlow()
105
.build()
106
107
// When expression
108
val whenCode = CodeBlock.builder()
109
.beginControlFlow("when (status)")
110
.addStatement("%T.PENDING -> %S", statusEnum, "Waiting for approval")
111
.addStatement("%T.APPROVED -> %S", statusEnum, "Request approved")
112
.addStatement("%T.REJECTED -> %S", statusEnum, "Request rejected")
113
.addStatement("else -> %S", "Unknown status")
114
.endControlFlow()
115
.build()
116
117
// Try-catch block
118
val tryCatchCode = CodeBlock.builder()
119
.beginControlFlow("try")
120
.addStatement("val result = riskyOperation()")
121
.addStatement("return result")
122
.nextControlFlow("catch (e: %T)", Exception::class)
123
.addStatement("logger.error(%S, e)", "Operation failed")
124
.addStatement("throw e")
125
.endControlFlow()
126
.build()
127
128
// Named parameters for complex formatting
129
val namedCode = CodeBlock.builder()
130
.addNamed(
131
"val \$field:L = \$type:T(\$value:S)",
132
mapOf(
133
"field" to "username",
134
"type" to String::class,
135
"value" to "john_doe"
136
)
137
)
138
.build()
139
140
// Joining code blocks
141
val parameters = listOf("name", "age", "email")
142
val paramCode = parameters.map { CodeBlock.of("%L", it) }
143
val joinedParams = CodeBlock.join(paramCode, ", ")
144
```
145
146
### Template Placeholders and Formatting
147
148
KotlinPoet supports various placeholder types for different kinds of code substitution.
149
150
```kotlin { .api }
151
// Placeholder types for CodeBlock.of() and related methods:
152
// %S - String literals (automatically escaped)
153
// %L - Literals (numbers, booleans, raw code)
154
// %N - Names (identifiers, avoiding keywords)
155
// %T - Types (TypeName instances, handles imports)
156
// %M - Members (MemberName instances, handles imports)
157
// %% - Literal percent sign
158
```
159
160
**Usage Examples:**
161
162
```kotlin
163
// String literals (%S) - automatically escaped
164
val stringLiteral = CodeBlock.of("val message = %S", "Hello \"World\"")
165
// Result: val message = "Hello \"World\""
166
167
// Literals (%L) - raw values
168
val numberLiteral = CodeBlock.of("val count = %L", 42)
169
val booleanLiteral = CodeBlock.of("val isActive = %L", true)
170
val rawCode = CodeBlock.of("val result = %L", "computeValue()")
171
172
// Names (%N) - identifiers that avoid keywords
173
val identifier = CodeBlock.of("val %N = %S", "class", "This would be escaped")
174
// Result: val `class` = "This would be escaped"
175
176
// Types (%T) - TypeName instances with import handling
177
val typeUsage = CodeBlock.of("val list = %T<%T>()", ArrayList::class, String::class)
178
// Result: val list = ArrayList<String>()
179
// Imports: import java.util.ArrayList, import kotlin.String
180
181
// Members (%M) - MemberName instances with import handling
182
val memberUsage = CodeBlock.of(
183
"return %M(items)",
184
MemberName.get(ClassName.get("kotlin.collections", "Collections"), "sort")
185
)
186
187
// Complex template with multiple placeholders
188
val complexTemplate = CodeBlock.of(
189
"val %N: %T = %T.%M(%S, %L)",
190
"result",
191
String::class,
192
String::class,
193
MemberName.get(String::class, "format"),
194
"Value: %d",
195
42
196
)
197
198
// Template with collections
199
val listTemplate = CodeBlock.of(
200
"val items = %T(%L)",
201
LIST,
202
listOf(1, 2, 3).joinToString(", ")
203
)
204
```
205
206
### Member Name Resolution
207
208
Manages references to class members (functions, properties, constants) with automatic import resolution.
209
210
```kotlin { .api }
211
/**
212
* Reference to a member (property, function, constructor, etc.)
213
*/
214
class MemberName {
215
/** The enclosing type that contains this member */
216
val enclosingClassName: ClassName?
217
218
/** The package name if this is a top-level member */
219
val packageName: String
220
221
/** The simple name of the member */
222
val simpleName: String
223
224
/** Whether this member can be imported */
225
val isExtension: Boolean
226
227
companion object {
228
/** Creates a member reference for a class member */
229
fun get(enclosingClassName: ClassName, simpleName: String): MemberName
230
231
/** Creates a member reference for a top-level member */
232
fun get(packageName: String, simpleName: String): MemberName
233
234
/** Creates a member reference from a Java Class and member name */
235
fun get(clazz: Class<*>, simpleName: String): MemberName
236
237
/** Creates a member reference from a KClass and member name */
238
fun get(klass: KClass<*>, simpleName: String): MemberName
239
}
240
}
241
```
242
243
**Usage Examples:**
244
245
```kotlin
246
// Class member references
247
val stringFormat = MemberName.get(String::class, "format")
248
val listOf = MemberName.get(ClassName.get("kotlin.collections", "Collections"), "listOf")
249
250
// Top-level function references
251
val println = MemberName.get("kotlin.io", "println")
252
val maxOf = MemberName.get("kotlin", "maxOf")
253
254
// Extension function references
255
val stringIsBlank = MemberName.get(String::class, "isBlank")
256
257
// Using member names in code
258
val memberUsageCode = CodeBlock.builder()
259
.addStatement("val greeting = %M(%S, name)", stringFormat, "Hello, %s!")
260
.addStatement("%M(greeting)", println)
261
.addStatement("val numbers = %M(1, 2, 3)", listOf)
262
.build()
263
264
// Member names for constants
265
val piConstant = MemberName.get(Math::class, "PI")
266
val maxValue = MemberName.get(Integer::class, "MAX_VALUE")
267
268
val constantUsage = CodeBlock.of(
269
"val area = %M * radius * radius",
270
piConstant
271
)
272
```
273
274
### Advanced Code Generation Patterns
275
276
Common patterns and techniques for generating complex code structures.
277
278
**Builder Pattern Generation:**
279
280
```kotlin
281
val builderClass = TypeSpec.classBuilder("PersonBuilder")
282
.addProperty(
283
PropertySpec.varBuilder("name", String::class.copy(nullable = true))
284
.addModifiers(KModifier.PRIVATE)
285
.initializer("null")
286
.build()
287
)
288
.addProperty(
289
PropertySpec.varBuilder("age", Int::class.copy(nullable = true))
290
.addModifiers(KModifier.PRIVATE)
291
.initializer("null")
292
.build()
293
)
294
.addFunction(
295
FunSpec.builder("name")
296
.addParameter("name", String::class)
297
.returns(ClassName("", "PersonBuilder"))
298
.addStatement("this.name = name")
299
.addStatement("return this")
300
.build()
301
)
302
.addFunction(
303
FunSpec.builder("age")
304
.addParameter("age", Int::class)
305
.returns(ClassName("", "PersonBuilder"))
306
.addStatement("this.age = age")
307
.addStatement("return this")
308
.build()
309
)
310
.addFunction(
311
FunSpec.builder("build")
312
.returns(ClassName("", "Person"))
313
.beginControlFlow("return %T(", ClassName("", "Person"))
314
.addStatement("name = checkNotNull(name) { %S }", "Name is required")
315
.addStatement("age = checkNotNull(age) { %S }", "Age is required")
316
.endControlFlow(")")
317
.build()
318
)
319
.build()
320
```
321
322
**Sealed Class Generation:**
323
324
```kotlin
325
val sealedResult = TypeSpec.classBuilder("Result")
326
.addModifiers(KModifier.SEALED)
327
.addTypeVariable(TypeVariableName.get("T"))
328
.addType(
329
TypeSpec.classBuilder("Success")
330
.addModifiers(KModifier.DATA)
331
.addTypeVariable(TypeVariableName.get("T"))
332
.superclass(
333
ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))
334
)
335
.primaryConstructor(
336
FunSpec.constructorBuilder()
337
.addParameter("data", TypeVariableName.get("T"))
338
.build()
339
)
340
.addProperty(
341
PropertySpec.builder("data", TypeVariableName.get("T"))
342
.initializer("data")
343
.build()
344
)
345
.build()
346
)
347
.addType(
348
TypeSpec.classBuilder("Error")
349
.addModifiers(KModifier.DATA)
350
.addTypeVariable(TypeVariableName.get("T"))
351
.superclass(
352
ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))
353
)
354
.primaryConstructor(
355
FunSpec.constructorBuilder()
356
.addParameter("exception", Throwable::class)
357
.build()
358
)
359
.addProperty(
360
PropertySpec.builder("exception", Throwable::class)
361
.initializer("exception")
362
.build()
363
)
364
.build()
365
)
366
.build()
367
```
368
369
**DSL Generation:**
370
371
```kotlin
372
val dslBuilder = TypeSpec.classBuilder("HtmlBuilder")
373
.addFunction(
374
FunSpec.builder("tag")
375
.addParameter("name", String::class)
376
.addParameter(
377
"block",
378
LambdaTypeName.get(
379
receiver = ClassName("", "TagBuilder"),
380
returnType = UNIT
381
)
382
)
383
.addStatement("val tagBuilder = TagBuilder(name)")
384
.addStatement("tagBuilder.block()")
385
.addStatement("append(tagBuilder.build())")
386
.build()
387
)
388
.addFunction(
389
FunSpec.builder("text")
390
.addParameter("content", String::class)
391
.addStatement("append(content)")
392
.build()
393
)
394
.build()
395
```
396
397
### Error Handling in Code Generation
398
399
Common patterns for handling errors and validation during code generation.
400
401
```kotlin
402
// Validation in generated code
403
val validatedProperty = PropertySpec.varBuilder("email", String::class)
404
.setter(
405
FunSpec.setterBuilder()
406
.addParameter("value", String::class)
407
.beginControlFlow("require(value.contains('@'))")
408
.addStatement("%S", "Invalid email format")
409
.endControlFlow()
410
.addStatement("field = value")
411
.build()
412
)
413
.build()
414
415
// Exception handling in generated methods
416
val safeOperation = FunSpec.builder("safeRead")
417
.addParameter("filename", String::class)
418
.returns(String::class.copy(nullable = true))
419
.beginControlFlow("return try")
420
.addStatement("%T(filename).readText()", File::class)
421
.nextControlFlow("catch (e: %T)", IOException::class)
422
.addStatement("null")
423
.endControlFlow()
424
.build()
425
```