JavaPoet is a Java API for generating .java source files programmatically with support for modern Java features including records and sealed types
CodeBlock represents an immutable fragment of code with formatting. It provides a fluent API for building code with proper indentation, control flow structures, and format string placeholders for type-safe code generation.
Create CodeBlock instances using factory methods.
/**
* Creates a CodeBlock from a format string
* @param format - Format string with placeholders ($L, $S, $T, $N)
* @param args - Arguments for format placeholders
* @return Immutable CodeBlock
*/
static CodeBlock of(String format, Object... args);
/**
* Joins multiple code blocks with a separator
* @param codeBlocks - Iterable of code blocks to join
* @param separator - Separator string between blocks
* @return Joined CodeBlock
*/
static CodeBlock join(Iterable<CodeBlock> codeBlocks, String separator);
/**
* Returns a collector for joining code blocks with separator
* @param separator - Separator string between blocks
* @return Collector for Stream operations
*/
static Collector<CodeBlock, ?, CodeBlock> joining(String separator);
/**
* Returns a collector with prefix, separator, and suffix
* @param separator - Separator string between blocks
* @param prefix - String to prepend
* @param suffix - String to append
* @return Collector for Stream operations
*/
static Collector<CodeBlock, ?, CodeBlock> joining(String separator, String prefix, String suffix);
/**
* Creates a new CodeBlock builder
* @return Builder for constructing a CodeBlock
*/
static CodeBlock.Builder builder();Usage Examples:
// Simple code block
CodeBlock simple = CodeBlock.of("return $S", "Hello");
// Join multiple blocks
List<CodeBlock> blocks = Arrays.asList(
CodeBlock.of("$S", "red"),
CodeBlock.of("$S", "green"),
CodeBlock.of("$S", "blue")
);
CodeBlock colors = CodeBlock.join(blocks, ", ");
// Using Stream collector
CodeBlock joined = Stream.of("a", "b", "c")
.map(s -> CodeBlock.of("$S", s))
.collect(CodeBlock.joining(", ", "[", "]"));
// Build complex code
CodeBlock complex = CodeBlock.builder()
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("total += i")
.endControlFlow()
.addStatement("return total")
.build();Check if a CodeBlock contains any code.
/**
* Checks if this code block is empty
* @return true if empty, false otherwise
*/
boolean isEmpty();Convert to a builder for modification.
/**
* Creates a builder initialized with this block's content
* @return Builder for modifying this CodeBlock
*/
CodeBlock.Builder toBuilder();The builder for constructing CodeBlock instances.
/**
* Checks if this builder is empty
* @return true if no code has been added, false otherwise
*/
boolean isEmpty();Add code using named argument placeholders.
/**
* Adds code with named arguments
* @param format - Format string with named placeholders (e.g., "$name:L")
* @param args - Map of argument names to values
* @return This builder for chaining
*/
CodeBlock.Builder addNamed(String format, Map<String, ?> args);Usage Example:
Map<String, Object> args = new HashMap<>();
args.put("field", "name");
args.put("value", "John");
CodeBlock code = CodeBlock.builder()
.addNamed("this.$field:N = $value:S", args)
.build();
// Generates: this.name = "John"Add code to the block using various methods.
/**
* Adds a CodeBlock to this builder
* @param codeBlock - CodeBlock to append
* @return This builder for chaining
*/
CodeBlock.Builder add(CodeBlock codeBlock);
/**
* Adds code from a format string
* @param format - Format string with placeholders
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
CodeBlock.Builder add(String format, Object... args);Usage Examples:
// Add with format string
CodeBlock code = CodeBlock.builder()
.add("$T value = ", String.class)
.add("$S", "hello")
.build();
// Add existing CodeBlock
CodeBlock existing = CodeBlock.of("$L + $L", 1, 2);
CodeBlock combined = CodeBlock.builder()
.add("int result = ")
.add(existing)
.build();Add complete statements (automatically adds semicolon and newline).
/**
* Adds a statement from a format string
* @param format - Format string for the statement
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
CodeBlock.Builder addStatement(String format, Object... args);
/**
* Adds a statement from a CodeBlock
* @param codeBlock - CodeBlock for the statement
* @return This builder for chaining
*/
CodeBlock.Builder addStatement(CodeBlock codeBlock);Usage Examples:
CodeBlock statements = CodeBlock.builder()
.addStatement("int count = 0")
.addStatement("$T.out.println($S)", System.class, "Hello")
.addStatement("return count")
.build();
// Generates:
// int count = 0;
// System.out.println("Hello");
// return count;Build control flow structures with proper indentation.
/**
* Begins a control flow block (if, for, while, etc.)
* @param controlFlow - Format string for control flow statement
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
CodeBlock.Builder beginControlFlow(String controlFlow, Object... args);
/**
* Adds next control flow (else if, else, catch, etc.)
* @param controlFlow - Format string for next control flow
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
CodeBlock.Builder nextControlFlow(String controlFlow, Object... args);
/**
* Ends the current control flow block
* @return This builder for chaining
*/
CodeBlock.Builder endControlFlow();
/**
* Ends control flow with a following statement (e.g., "while" for do-while)
* @param controlFlow - Format string for ending statement
* @param args - Arguments for format placeholders
* @return This builder for chaining
*/
CodeBlock.Builder endControlFlow(String controlFlow, Object... args);Usage Examples:
// If-else
CodeBlock ifElse = CodeBlock.builder()
.beginControlFlow("if (value > 0)")
.addStatement("return true")
.nextControlFlow("else")
.addStatement("return false")
.endControlFlow()
.build();
// For loop
CodeBlock forLoop = CodeBlock.builder()
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("$T.out.println(i)", System.class)
.endControlFlow()
.build();
// While loop
CodeBlock whileLoop = CodeBlock.builder()
.beginControlFlow("while (iterator.hasNext())")
.addStatement("process(iterator.next())")
.endControlFlow()
.build();
// Try-catch-finally
CodeBlock tryCatch = CodeBlock.builder()
.beginControlFlow("try")
.addStatement("riskyOperation()")
.nextControlFlow("catch ($T e)", IOException.class)
.addStatement("$T.err.println(e)", System.class)
.nextControlFlow("finally")
.addStatement("cleanup()")
.endControlFlow()
.build();
// Do-while
CodeBlock doWhile = CodeBlock.builder()
.beginControlFlow("do")
.addStatement("process()")
.endControlFlow("while (hasMore())")
.build();
// Switch statement
CodeBlock switchStmt = CodeBlock.builder()
.beginControlFlow("switch (value)")
.add("case 1:\n")
.indent()
.addStatement("handleOne()")
.addStatement("break")
.unindent()
.add("case 2:\n")
.indent()
.addStatement("handleTwo()")
.addStatement("break")
.unindent()
.add("default:\n")
.indent()
.addStatement("handleDefault()")
.unindent()
.endControlFlow()
.build();Manually control indentation levels.
/**
* Increases indentation level
* @return This builder for chaining
*/
CodeBlock.Builder indent();
/**
* Decreases indentation level
* @return This builder for chaining
*/
CodeBlock.Builder unindent();Usage Example:
CodeBlock indented = CodeBlock.builder()
.addStatement("public void method()")
.indent()
.addStatement("int x = 1")
.addStatement("int y = 2")
.unindent()
.addStatement("}")
.build();Clear all content from the builder.
/**
* Clears all content from this builder
* @return This builder for chaining
*/
CodeBlock.Builder clear();Build the final CodeBlock instance.
/**
* Builds the CodeBlock with configured content
* @return The built CodeBlock instance
*/
CodeBlock build();JavaPoet supports special placeholders in format strings:
Usage Examples:
// $L for literals
CodeBlock literals = CodeBlock.of("int value = $L", 42);
// Generates: int value = 42
// $S for strings (adds quotes and escapes)
CodeBlock strings = CodeBlock.of("String msg = $S", "Hello \"World\"");
// Generates: String msg = "Hello \"World\""
// $T for types (auto-imports)
CodeBlock types = CodeBlock.of("$T list = new $T<>()", List.class, ArrayList.class);
// Generates: List list = new ArrayList<>()
// Auto-imports: java.util.List, java.util.ArrayList
// $N for names
MethodSpec method = MethodSpec.methodBuilder("helper").build();
CodeBlock names = CodeBlock.of("$N()", method);
// Generates: helper()
// $$ for dollar signs
CodeBlock dollars = CodeBlock.of("String pattern = $S", "Price: $$10");
// Generates: String pattern = "Price: $10"// Positional arguments (1-based indexing)
CodeBlock positional = CodeBlock.of("I ate $2L $1L", "tacos", 3);
// Generates: I ate 3 tacos
// Named arguments
Map<String, Object> args = new HashMap<>();
args.put("food", "tacos");
args.put("count", 3);
CodeBlock named = CodeBlock.builder()
.addNamed("I ate $count:L $food:L", args)
.build();
// Generates: I ate 3 tacosCodeBlock declaration = CodeBlock.builder()
.addStatement("$T name = $S", String.class, "John")
.addStatement("int age = $L", 30)
.addStatement("$T items = new $T<>()",
ParameterizedTypeName.get(List.class, String.class),
ArrayList.class)
.build();CodeBlock calls = CodeBlock.builder()
.addStatement("$T.out.println($S)", System.class, "Hello")
.addStatement("list.add($S)", "item")
.addStatement("process(value, $L)", 42)
.build();CodeBlock returns = CodeBlock.builder()
.addStatement("return $S", "result")
.build();
CodeBlock conditionalReturn = CodeBlock.builder()
.beginControlFlow("if (value != null)")
.addStatement("return value")
.nextControlFlow("else")
.addStatement("return $S", "default")
.endControlFlow()
.build();CodeBlock expression = CodeBlock.builder()
.add("$T result = stream.filter(x -> x > 0)\n", int.class)
.indent()
.add(".map(x -> x * 2)\n")
.add(".reduce(0, (a, b) -> a + b);\n")
.unindent()
.build();class CodeBlock {
static CodeBlock of(String format, Object... args);
static CodeBlock join(Iterable<CodeBlock> codeBlocks, String separator);
static Collector<CodeBlock, ?, CodeBlock> joining(String separator);
static Collector<CodeBlock, ?, CodeBlock> joining(String separator, String prefix, String suffix);
static Builder builder();
boolean isEmpty();
Builder toBuilder();
boolean equals(Object o);
int hashCode();
String toString();
}
class CodeBlock.Builder {
boolean isEmpty();
CodeBlock.Builder addNamed(String format, Map<String, ?> args);
CodeBlock.Builder add(CodeBlock codeBlock);
CodeBlock.Builder add(String format, Object... args);
CodeBlock.Builder addStatement(String format, Object... args);
CodeBlock.Builder addStatement(CodeBlock codeBlock);
CodeBlock.Builder beginControlFlow(String controlFlow, Object... args);
CodeBlock.Builder nextControlFlow(String controlFlow, Object... args);
CodeBlock.Builder endControlFlow();
CodeBlock.Builder endControlFlow(String controlFlow, Object... args);
CodeBlock.Builder indent();
CodeBlock.Builder unindent();
CodeBlock.Builder clear();
CodeBlock build();
}Install with Tessl CLI
npx tessl i tessl/maven-com-palantir-javapoet--javapoetdocs