public class User { String username; String firstname; String lastname; int age; public String getUsername() { return username; } public String getFirstname() { return firstname; } public String getLastname() { return lastname; } public int getAge() { return age; } }
Code Generation • Pair well with Annotation Processors • Used to generate new Java source files • JavaPoet represents Java source as model https://github.com/square/javapoet
JavaPoet public final class UserBuilder String builderName = String.format("%sBuilder", type.getSimpleName()); ClassName builderType = ClassName.get(packageName, builderName);
JavaPoet TypeSpec builder = TypeSpec.classBuilder(builderType) .addModifiers(PUBLIC, FINAL) .addField(username) .addMethod(usernameSetter) .build(); Modifiers Name Content public final class UserBuilder {\ }\
JavaPoet Modifiers Name Content TypeSpec builder = TypeSpec.classBuilder(builderType) .addModifiers(PUBLIC, FINAL) .addField(username) .addMethod(usernameSetter) .build(); public final class UserBuilder {\ }\
JavaPoet Modifiers Name Content TypeSpec builder = TypeSpec.classBuilder(builderType) .addModifiers(PUBLIC, FINAL) .addField(username) .addMethod(usernameSetter) .build(); public final class UserBuilder {\ }\
JavaPoet Modifiers Name Content TypeSpec builder = TypeSpec.classBuilder(builderType) .addModifiers(PUBLIC, FINAL) .addField(username) .addMethod(usernameSetter) .build(); public final class UserBuilder {\ }\
@Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) { // get element metadata // create private fields and public setters // create the build method // create the builder type // write the java source file } } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
// create private fields and public setters List fields = new ArrayList(vars.size()); List setters = new ArrayList(vars.size()); for (VariableElement var : vars) { TypeName typeName = TypeName.get(var.asType()); String name = var.getSimpleName().toString(); // create the field fields.add(FieldSpec.builder(typeName, name, PRIVATE).build()); // create the setter setters.add(MethodSpec.methodBuilder(name) .addModifiers(PUBLIC) .returns(builderType) .addParameter(typeName, name) .addStatement("this.$N = $N", name, name) .addStatement("return this") .build()); } Annotation Processor
@Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) { // get element metadata // create private fields and public setters // create the build method // create the builder type // write the java source file } } Annotation Processor
Using our Annotation @Builder public class User { String username; String firstName; String lastName; int age; } User user = new UserBuilder() .username("rharter") .firstName("Ryan") .lastName("Harter") .age(30) .build();