Upgrade to Pro — share decks privately, control downloads, hide ads and more …

@Eliminate("Boilerplate")

 @Eliminate("Boilerplate")

Using Java annotations to reduce boilerplate code.

Ryan Harter

July 28, 2016
Tweet

More Decks by Ryan Harter

Other Decks in Technology

Transcript

  1. @Eliminate(“Boilerplate”)
    Ryan Harter
    @rharter

    View Slide

  2. JAVA HAS BOILERPLATE

    View Slide

  3. JAVA IS BOILERPLATE

    View Slide

  4. 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;
    }
    }

    View Slide

  5. public final class UserBuilder {
    private String username;
    private String firstName;
    private String lastName;
    private int age;
    public UserBuilder() {
    }\
    public UserBuilder username(String username) {
    this.username = username;
    return this;
    }\
    public UserBuilder firstName(String firstName) {
    this.firstName = firstName;
    return this;
    }\

    View Slide

  6. return this;
    }\
    public UserBuilder lastName(String lastName) {
    this.lastName = lastName;
    return this;
    }\
    public UserBuilder age(int age) {
    this.age = age;
    return this;
    }\
    public User build() {
    User user = new User();
    user.username = this.username;
    user.firstName = this.firstName;
    user.lastName = this.lastName;
    user.age = this.age;
    return user;
    }\
    }\

    View Slide

  7. Annotation Processing
    +
    Code Generation

    View Slide

  8. Annotation Processing

    View Slide

  9. Annotation Processing

    View Slide

  10. Annotation Processing
    • Part of javac

    View Slide

  11. Annotation Processing
    • Part of javac
    • Read @Annotated source

    View Slide

  12. Annotation Processing
    • Part of javac
    • Read @Annotated source
    • Generate .java source files

    View Slide

  13. Benefits

    View Slide

  14. Benefits
    • Write your code generator once

    View Slide

  15. Benefits
    • Write your code generator once
    • Trust the generated code

    View Slide

  16. Components

    View Slide

  17. Components
    • Annotations

    View Slide

  18. Components
    • Annotations
    • Processor

    View Slide

  19. Components
    • Annotations
    • Processor
    • APT (Annotation Processing Tool)

    View Slide

  20. Components
    • Annotations
    • Processor
    • APT (Annotation Processing Tool)
    • Annotated Source

    View Slide

  21. Annotations
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Builder {
    }

    View Slide

  22. Annotations
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Builder {
    }

    View Slide

  23. Annotations
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Builder {
    }

    View Slide

  24. Annotations
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Builder {
    }

    View Slide

  25. Annotation Processor
    public class FooProcessor extends AbstractProcessor {
    }\\

    View Slide

  26. Annotation Processor
    public class FooProcessor extends AbstractProcessor {
    public FooProcessor() {
    super();
    }\
    }\\

    View Slide

  27. Annotation Processor
    public class FooProcessor extends AbstractProcessor {
    private Messager messager;
    private Filer filer;

    @Override public synchronized void init(
    ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.messager = processingEnv.getMessager();
    this.filer = processingEnv.getFiler();
    }\
    }\\

    View Slide

  28. Annotation Processor
    public class FooProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
    }\
    }\\

    View Slide

  29. Annotation Processor
    public class FooProcessor extends AbstractProcessor {

    @Override public Set getSupportedAnnotationTypes() {
    return ImmutableSet.of(Builder.class.getCanonicalName());
    }
    }\\

    View Slide

  30. Annotation Processor
    public class FooProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    ...
    }
    }\\

    View Slide

  31. Annotation Processor
    public class FooProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    ...
    }
    }

    View Slide

  32. Processing Rounds
    Java Sources APT Completed

    View Slide

  33. Processing Rounds
    Java Sources APT Completed
    Round 1

    View Slide

  34. Processing Rounds
    Java Sources APT Completed
    Round 1

    View Slide

  35. Processing Rounds
    Java Sources APT Completed
    Round 1

    View Slide

  36. Processing Rounds
    Java Sources APT Completed
    Round 1

    View Slide

  37. Processing Rounds
    Java Sources APT Completed
    Round 2

    View Slide

  38. Processing Rounds
    Java Sources APT Completed
    Round 2

    View Slide

  39. Processing Rounds
    Java Sources APT Completed
    Complete

    View Slide

  40. Annotation Processor
    • ServiceLoader Discovery File
    com.ryanharter.example.annotations.BuilderProcessor
    META-INF/services/javax.annotation.processing.Processor

    View Slide

  41. Add to APT Classpath
    dependencies {
    compile project(':annotation')
    apt project(':processor')
    }
    app/build.gradle
    https://github.com/tbroyer/gradle-apt-plugin
    https://bitbucket.org/hvisser/android-apt

    View Slide

  42. Code Generation

    View Slide

  43. Code Generation

    View Slide

  44. Code Generation
    • Pair well with Annotation Processors

    View Slide

  45. Code Generation
    • Pair well with Annotation Processors
    • Used to generate new Java source files

    View Slide

  46. Code Generation
    • Pair well with Annotation Processors
    • Used to generate new Java source files
    • JavaPoet represents Java source as model

    View Slide

  47. 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

    View Slide

  48. JavaPoet
    https://github.com/square/javapoet

    View Slide

  49. JavaPoet
    • Uses Fluent API with builders
    https://github.com/square/javapoet

    View Slide

  50. JavaPoet
    • Uses Fluent API with builders
    • Based on Specs
    https://github.com/square/javapoet

    View Slide

  51. JavaPoet
    • Uses Fluent API with builders
    • Based on Specs
    • TypeSpec
    https://github.com/square/javapoet

    View Slide

  52. JavaPoet
    • Uses Fluent API with builders
    • Based on Specs
    • TypeSpec
    • MethodSpec
    https://github.com/square/javapoet

    View Slide

  53. JavaPoet
    • Uses Fluent API with builders
    • Based on Specs
    • TypeSpec
    • MethodSpec
    • ParameterSpec
    https://github.com/square/javapoet

    View Slide

  54. JavaPoet
    • Uses Fluent API with builders
    • Based on Specs
    • TypeSpec
    • MethodSpec
    • ParameterSpec
    • FieldSpec
    https://github.com/square/javapoet

    View Slide

  55. JavaPoet
    public final class UserBuilder {
    // fields
    private String username;

    // methods
    public UserBuilder username(String username) {
    this.username = username;
    return this;
    }

    }\

    View Slide

  56. JavaPoet
    public final class UserBuilder {
    // fields
    private String username;

    // methods
    public UserBuilder username(String username) {
    this.username = username;
    return this;
    }

    }\

    View Slide

  57. JavaPoet
    public final class UserBuilder
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);

    View Slide

  58. JavaPoet
    public final class UserBuilder {
    // fields
    private String username;

    // methods
    public UserBuilder username(String username) {
    this.username = username;
    return this;
    }

    }\

    View Slide

  59. private String username;
    JavaPoet

    View Slide

  60. JavaPoet
    Modifiers
    private String username;

    View Slide

  61. JavaPoet
    Modifiers Type
    private String username;

    View Slide

  62. JavaPoet
    Modifiers Type
    private String username;
    Name

    View Slide

  63. JavaPoet
    FieldSpec username = FieldSpec
    .builder(String.class, "username", Modifier.PRIVATE)
    .build();
    private String username;
    Modifiers Type
    private String username;
    Name

    View Slide

  64. private String username;
    JavaPoet
    Modifiers Type Name
    FieldSpec username = FieldSpec
    .builder(String.class, "username", Modifier.PRIVATE)
    .build();
    private String username;

    View Slide

  65. JavaPoet
    FieldSpec username = FieldSpec
    .builder(String.class, "username", Modifier.PRIVATE)
    .build();
    private String username;

    View Slide

  66. JavaPoet
    public final class UserBuilder {\
    // fields
    private String username;

    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    }\

    View Slide

  67. JavaPoet
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  68. JavaPoet
    Modifiers
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  69. JavaPoet
    Modifiers Return Type
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  70. JavaPoet
    Modifiers Return Type Name
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  71. JavaPoet
    Modifiers Return Type Name Parameters
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  72. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  73. JavaPoet
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  74. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    View Slide

  75. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();

    View Slide

  76. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();

    View Slide

  77. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();

    View Slide

  78. JavaPoet
    Modifiers Return Type Name Parameters
    Statements
    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();

    View Slide

  79. // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\
    JavaPoet
    MethodSpec usernameSetter = MethodSpec.methodBuilder("username")
    .addModifiers(PUBLIC)
    .returns(builderType)
    .addParameter(String.class, "username")
    .addStatement("this.$N = username", username)
    .addStatement("return this")
    .build();

    View Slide

  80. JavaPoet
    public final class UserBuilder {\
    // fields
    private String username;

    // methods
    public UserBuilder username(String username) {\
    this.username = username;
    return this;
    }\

    }\

    View Slide

  81. JavaPoet
    public final class UserBuilder {\
    }\

    View Slide

  82. JavaPoet
    Modifiers
    public final class UserBuilder {\
    }\

    View Slide

  83. JavaPoet
    Modifiers Name
    public final class UserBuilder {\
    }\

    View Slide

  84. JavaPoet
    Modifiers Name
    Content
    public final class UserBuilder {\
    }\

    View Slide

  85. JavaPoet
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addField(username)
    .addMethod(usernameSetter)
    .build();
    Modifiers Name
    Content
    public final class UserBuilder {\
    }\

    View Slide

  86. JavaPoet
    Modifiers Name
    Content
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addField(username)
    .addMethod(usernameSetter)
    .build();
    public final class UserBuilder {\
    }\

    View Slide

  87. JavaPoet
    Modifiers Name
    Content
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addField(username)
    .addMethod(usernameSetter)
    .build();
    public final class UserBuilder {\
    }\

    View Slide

  88. JavaPoet
    Modifiers Name
    Content
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addField(username)
    .addMethod(usernameSetter)
    .build();
    public final class UserBuilder {\
    }\

    View Slide

  89. JavaPoet
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addField(username)
    .addMethod(usernameSetter)
    .build();
    public final class UserBuilder {\
    }\

    View Slide

  90. Annotation Processor
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    }

    View Slide

  91. @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) {
    }
    }
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) {
    }
    }
    Annotation Processor

    View Slide

  92. @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) {
    }
    }
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment roundEnv) {
    for (Element el : roundEnv.getElementsAnnotatedWith(Builder.class)) {
    }
    }
    Annotation Processor

    View Slide

  93. @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

    View Slide

  94. // get element metadata
    String packageName = getPackageName(type);
    String targetName = lowerCamelCase(type.getSimpleName().toString());
    Set vars = getNonPrivateVariables(type);
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);
    Annotation Processor

    View Slide

  95. // get element metadata
    String packageName = getPackageName(type);
    String targetName = lowerCamelCase(type.getSimpleName().toString());
    Set vars = getNonPrivateVariables(type);
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);
    Annotation Processor

    View Slide

  96. // get element metadata
    String packageName = getPackageName(type);
    String targetName = lowerCamelCase(type.getSimpleName().toString());
    Set vars = getNonPrivateVariables(type);
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);
    Annotation Processor

    View Slide

  97. // get element metadata
    String packageName = getPackageName(type);
    String targetName = lowerCamelCase(type.getSimpleName().toString());
    Set vars = getNonPrivateVariables(type);
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);
    Annotation Processor

    View Slide

  98. // get element metadata
    String packageName = getPackageName(type);
    String targetName = lowerCamelCase(type.getSimpleName().toString());
    Set vars = getNonPrivateVariables(type);
    String builderName = String.format("%sBuilder", type.getSimpleName());
    ClassName builderType = ClassName.get(packageName, builderName);
    Annotation Processor

    View Slide

  99. // 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

    View Slide

  100. // 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

    View Slide

  101. // 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

    View Slide

  102. // 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

    View Slide

  103. // 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

    View Slide

  104. // 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

    View Slide

  105. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  106. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  107. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  108. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  109. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  110. // create the build method
    TypeName targetType = TypeName.get(type.asType());
    MethodSpec.Builder buildMethodBuilder =
    MethodSpec.methodBuilder("build")
    .addModifiers(PUBLIC)
    .returns(targetType)
    .addStatement("$1T $2N = new $1T()", targetType, targetName);
    for (FieldSpec field : fields) {
    buildMethodBuilder
    .addStatement("$1N.$2N = this.$2N", targetName, field);
    }
    buildMethodBuilder.addStatement("return $N", targetName);
    MethodSpec buildMethod = buildMethodBuilder.build();
    Annotation Processor

    View Slide

  111. Annotation Processor
    // create the builder
    TypeSpec builder = TypeSpec.classBuilder(builderType)
    .addModifiers(PUBLIC, FINAL)
    .addFields(fields)
    .addMethods(setters)
    .addMethod(buildMethod)
    .build();

    View Slide

  112. // write java source file
    JavaFile file = JavaFile
    .builder(builderType.packageName(), builder.build())
    .build();
    try {
    file.writeTo(filer);
    } catch (IOException e) {
    messager.printMessage(Diagnostic.Kind.ERROR,
    "Failed to write file for element", el);
    }
    Annotation Processor

    View Slide

  113. @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

    View Slide

  114. Using our Annotation
    public class User {/
    String username;/
    String firstName;/
    String lastName;/
    int age;/
    }/

    View Slide

  115. Using our Annotation
    @Builder public class User {/
    String username;/
    String firstName;/
    String lastName;/
    int age;/
    }/

    View Slide

  116. 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();

    View Slide

  117. References
    • Android APT Plugin - https://bitbucket.org/hvisser/android-apt
    • Gradle APT Plugin - https://github.com/tbroyer/gradle-apt-plugin
    • JavaPoet - https://github.com/square/javapoet

    View Slide

  118. Find Me
    • @rharter
    • http://ryanharter.com

    View Slide