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

Annotation Processing 101

Annotation Processing 101

Writing Java application can be annoying because the Java programming language requires to write boilerplate code. Writing Android apps makes no difference. Moreover, on Android you have to write a lot of code for doing simple things like setting up a Fragment with arguments, implement the Parcelable interface, define ViewHolder classes for RecyclerViews and so on.

Fortunately, the writing of boilerplate code can be reduced by using Annotation processing, a feature of the Java compiler that allows to setup hooks to generate code based on annotations.

The aim of the talk is to understand the annotation processing fundamentals and to be able to write an annotation processor. Furthermore, existing annotation processors for Android will be showcased.

Video Link: https://www.youtube.com/watch?v=43FFfTyDYEg

Hannes Dorfmann

June 04, 2015
Tweet

More Decks by Hannes Dorfmann

Other Decks in Programming

Transcript

  1. Annotation Processing 101
    Hannes Dorfmann
    @sockeqwe
    +HannesDorfmann
    hannesdorfmann.com

    View Slide

  2. Annotation Processing
    ● Java 5 (Sep. 2004)
    ● JSR 269: 6 months after Java 5 released
    ● Plugin system for Annotation Processors

    View Slide

  3. Annotation Processing
    ● Part of javac
    ● @Annotation based
    ● Generates java source code files

    View Slide

  4. Reduces writing
    boilerplate code

    View Slide

  5. public class MyActivity extends Activity {
    @InjectView(R.id.title) TextView title;
    @InjectView(R.id.text) TextView text;
    @InjectView(R.id.submit) Button submit;
    @InjectView(R.id.imageView) ImageView image;
    }
    Butter Knife
    http://jakewharton.github.io/butterknife/

    View Slide

  6. public class MyActivity extends Activity {
    @Icicle String username;
    @Icicle int counter;
    }
    Icepick
    https://github.com/frankiesardo/icepick

    View Slide

  7. public class ItemFragment extends Fragment {
    @Arg long itemId;
    @Arg int categoryId;
    }
    FragmentArgs
    https://github.com/sockeqwe/fragmentargs

    View Slide

  8. @Parcel
    public class Person {
    String firstname;
    String lastname;
    int age;
    }
    Parceler
    http://parceler.org

    View Slide

  9. public class AnimalsAdapter extends SupportAnnotatedAdapter
    implements AnimalsAdapterBinder {
    @ViewType(layout = R.layout.list_dog)
    public final int dog = 0;
    @ViewType(layout = R.layout.list_cat)
    public final int cat = 1;
    AnnotatedAdapter

    View Slide

  10. public class AnimalsAdapter extends SupportAnnotatedAdapter
    implements AnimalsAdapterBinder {
    @Override
    public void bindViewHolder(DogViewHolder vh, int position) {
    Dog dog = (Dog) animals.get(position);
    vh.name.setText(dog.getName());
    }
    AnnotatedAdapter

    View Slide

  11. public class AnimalsAdapter extends SupportAnnotatedAdapter
    implements AnimalsAdapterBinder {
    @Override
    public void bindViewHolder(CatViewHolder vh, int position) {
    Cat cat = (Cat) animals.get(position);
    vh.image.setImageBitmap(cat.getImage());
    }
    }
    AnnotatedAdapter
    https://github.com/sockeqwe/AnnotatedAdapter

    View Slide

  12. Performance

    View Slide

  13. Performance
    ● Compile time code generation
    ● Full JVM
    ● “Native” java code (as hand-written)
    ● No reflections!

    View Slide

  14. LoganSquare
    https://github.com/bluelinelabs/LoganSquare
    Parsing 60 items
    68 ms
    Gson
    Jackson
    LoganSquare
    76 ms
    Serializing 60 items
    74 ms
    60 ms

    View Slide

  15. LoganSquare
    https://github.com/bluelinelabs/LoganSquare
    Parsing 60 items
    68 ms
    Gson
    Jackson
    LoganSquare
    76 ms
    17
    Serializing 60 items
    74 ms
    60 ms
    10

    View Slide

  16. Dagger 2
    ● Google Scale
    ● Replacing Dagger 1 with Dagger 2
    ● Saving 13 % CPU by just moving DI
    https://www.youtube.com/watch?v=oK_XtfXPkqw (36:00)

    View Slide

  17. Otto 1.3.7
    https://github.com/greenrobot/EventBus
    EventBus 2.4
    2000
    4000
    18000
    16000
    8000
    12000
    EventBus 3.0

    View Slide

  18. Otto 1.3.7
    https://github.com/greenrobot/EventBus
    EventBus 2.4 EventBus 3.0
    2000
    4000
    18000
    16000
    8000
    12000
    EventBus 3.0

    View Slide

  19. Limitation
    ● Can generate only new files
    ● Can’t manipulate already existing files
    ○ Bytecode manipulation (ASM, Afterburn)
    ○ AST manipulation (Project Lombok)

    View Slide

  20. Not debuggable!

    View Slide

  21. Example
    Pizza Store

    View Slide

  22. public interface Meal {
    public float getPrice();
    }
    public class MargheritaPizza implements Meal {
    @Override public float getPrice() {
    return 6f;
    }
    }

    View Slide

  23. public class CalzonePizza implements Meal {
    @Override public float getPrice() {
    return 8.5f;
    }
    }
    public class Tiramisu implements Meal {
    @Override public float getPrice() {
    return 4.5f;
    }
    }

    View Slide

  24. public class PizzaStore {
    private MealFactory factory = new MealFactory();
    public static void main(String[] args) throws IOException {
    PizzaStore pizzaStore = new PizzaStore();
    Meal meal = pizzaStore.order( readConsole() );
    System.out.println("Bill: $" + meal.getPrice());
    }
    public Meal order(String mealName) {
    return factory.create(mealName);
    }
    }

    View Slide

  25. public class MealFactory {
    public Meal create(String id) {
    if ("Margherita".equals(id)) return new MargheritaPizza();
    if ("Calzone".equals(id)) return new CalzonePizza();
    if ("Tiramisu".equals(id)) return new Tiramisu();
    throw new IllegalArgumentException("Unknown meal”);
    }
    }

    View Slide

  26. @Target(ElementType.TYPE)
    public @interface Factory {
    String id();
    Class type();
    }

    View Slide

  27. @Factory(
    id = "Margherita",
    type = Meal.class
    )
    public class MargheritaPizza implements Meal {
    @Override
    public float getPrice() {
    return 6f;
    }
    }

    View Slide

  28. @Factory(
    id = "Calzone",
    type = Meal.class
    )
    public class CalzonePizza implements Meal {
    @Override
    public float getPrice() {
    return 8.5f;
    }
    }

    View Slide

  29. Pizza Store Factory
    1. Only classes can be @Factory

    View Slide

  30. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor

    View Slide

  31. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory

    View Slide

  32. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group

    View Slide

  33. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group
    5. @Factory classes must inherit from specified type

    View Slide

  34. @Factory(
    id = "Margherita",
    type = Meal.class
    )
    public class MargheritaPizza implements Meal {
    @Override
    public float getPrice() {
    return 6f;
    }
    }

    View Slide

  35. @Factory(
    id = "Margherita",
    type = Meal.class
    )
    public class MargheritaPizza implements Meal {
    @Override
    public float getPrice() {
    return 6f;
    }
    }

    View Slide

  36. public class FactoryProcessor extends AbstractProcessor {
    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private Messager messager;
    private Map factoryClasses;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
    typeUtils = processingEnv.getTypeUtils();
    elementUtils = processingEnv.getElementUtils();
    filer = processingEnv.getFiler();
    messager = processingEnv.getMessager();
    }

    View Slide

  37. public class FactoryProcessor extends AbstractProcessor {
    @Override
    public Set getSupportedAnnotationTypes() {
    Set annotations = new LinkedHashSet();
    annotations.add(Factory.class.getCanonicalName());
    return annotations;
    }

    View Slide

  38. public class FactoryProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    }
    }
    }

    View Slide

  39. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }

    View Slide

  40. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement

    View Slide

  41. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement

    View Slide

  42. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement

    View Slide

  43. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement
    ExecuteableElement

    View Slide

  44. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement
    ExecuteableElement
    ExecuteableElement

    View Slide

  45. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement
    ExecuteableElement
    ExecuteableElement
    TypeElement

    View Slide

  46. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement
    ExecuteableElement
    ExecuteableElement
    TypeElement

    View Slide

  47. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeElement
    VariableElement
    VariableElement
    ExecuteableElement
    ExecuteableElement
    TypeElement

    View Slide

  48. @Example
    public class Foo {
    private int a;
    private Other other;
    public Foo () { ... }
    public void setA (
    int newA
    ) { ... }
    }
    TypeMirror

    View Slide

  49. public class FactoryProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv);
    @Override
    public Set getSupportedAnnotationTypes();
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment rv);
    }

    View Slide

  50. FactoryProcessor.jar
    - com
    - example
    - FactoryProcessor.class
    - META-INF
    - services
    - javax.annotation.processing.Processor

    View Slide

  51. @AutoService(Processor.class)
    public class FactoryProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv);
    @Override
    public Set getSupportedAnnotationTypes();
    @Override
    public boolean process(Set extends TypeElement> annotations,
    RoundEnvironment rv);
    }

    View Slide

  52. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group
    5. @Factory classes must inherit from specified type

    View Slide

  53. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (! (annotatedElement instanceof TypeElement) ){
    ...
    }
    }
    }

    View Slide

  54. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (! (annotatedElement instanceof TypeElement) ){
    ...
    }
    }
    }

    View Slide

  55. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    messager.printMessage(
    Diagnostic.Kind.ERROR,
    "Only classes can be annotated with @Factory",
    annotatedElement );
    }
    }
    }

    View Slide

  56. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    error( annotatedElement, "Only classes can be annotated with @Factory");
    }
    }
    }
    public void error(Element e, String msg) {
    messager.printMessage(Diagnostic.Kind.ERROR, "Error", e);
    }

    View Slide

  57. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    error( annotatedElement, "Only classes can be annotated with @Factory");
    annotatedElement = null;
    }
    annotatedElement.getSimpleName();
    }
    }

    View Slide

  58. Caused by: java.lang.NullPointerException
    at com.example.processor.FactoryProcessor.process(FactoryProcessor.java:161)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor
    (JavacProcessingEnvironment.java:793)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.
    discoverAndRunProcs(JavacProcessingEnvironment.java:722)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1700
    (JavacProcessingEnvironment.java:97)
    ... 32 more

    View Slide

  59. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    error( annotatedElement, "Only classes can be annotated with @Factory");
    annotatedElement = null;
    }
    annotatedElement.getSimpleName();
    }
    }

    View Slide

  60. public boolean process(Set<> annotations, RoundEnvironment rv) {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    error( annotatedElement, "Only classes can be annotated with @Factory");
    }
    checkOtherThings( annotatedElement );
    }
    }
    public void checkOtherThings(Element element) { ... }

    View Slide

  61. public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    if (annotatedElement.getKind() != ElementKind.CLASS) {
    error( annotatedElement, "Only classes can be annotated with @Factory");
    }
    checkOtherThings( annotatedElement );
    }
    } catch (ProcessingException e) {
    error(e.getElement(), e.getMessage());
    }
    }

    View Slide

  62. public void checkOtherThings(Element element) throws ProcessingException

    View Slide

  63. public void checkOtherThings(Element element) throws ProcessingException
    public class ProcessingException extends Exception {
    Element element;
    public ProcessingException(Element element, String msg, Object... args) {
    super(String.format(msg, args));
    this.element = element;
    }
    public Element getElement() {
    return element;
    }
    }

    View Slide

  64. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group
    5. @Factory classes must inherit from specified type

    View Slide

  65. public void checkConstructor(TypeElement e) throws ProcessingException {
    for (Element enclosed : e.getEnclosedElements()) {
    if (enclosed.getKind() == ElementKind.CONSTRUCTOR) {
    ExecutableElement constructorElement = (ExecutableElement) enclosed;
    if (constructorElement.getParameters().size() == 0 &&
    constructorElement.getModifiers().contains(Modifier.PUBLIC)) {
    return;
    }
    }
    throw new ProcessingException(e, "No public empty constructor");
    }

    View Slide

  66. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group
    5. @Factory classes must inherit from specified type

    View Slide

  67. public class FactoryAnnotatedClass {
    TypeElement annotatedClassElement;
    String id;
    String qualifiedGroupClassName;
    public FactoryAnnotatedClass(TypeElement element) throws ProcessingException {
    this.annotatedClassElement = classElement;
    }
    }

    View Slide

  68. public FactoryAnnotatedClass(TypeElement element) throws ProcessingException {
    Factory annotation = classElement.getAnnotation(Factory.class);
    this.id = annotation.id();
    if (StringUtils.isEmpty(id)) {
    throw new ProcessingException(classElement,
    "id() is null or empty!);
    }
    }

    View Slide

  69. public FactoryAnnotatedClass(TypeElement element) throws ProcessingException {
    Factory annotation = classElement.getAnnotation(Factory.class);
    try {
    Class> clazz = annotation.type();
    qualifiedGroupClassName = clazz.getCanonicalName();
    } catch (MirroredTypeException mte) {
    }
    }

    View Slide

  70. public FactoryAnnotatedClass(TypeElement element) throws ProcessingException {
    Factory annotation = classElement.getAnnotation(Factory.class);
    try {
    Class> clazz = annotation.type();
    qualifiedGroupClassName = clazz.getCanonicalName();
    } catch (MirroredTypeException mte) {
    DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
    TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
    qualifiedGroupClassName = classTypeElement.getQualifiedName().toString();
    }
    }

    View Slide

  71. public class FactoryGroupedClasses {
    String qualifiedClassName;
    Map itemsMap =
    new LinkedHashMap();
    public FactoryGroupedClasses(String qualifiedClassName) {
    this.qualifiedClassName = qualifiedClassName;
    }
    }

    View Slide

  72. public class FactoryGroupedClasses {
    public void add(FactoryAnnotatedClass toInsert) throws ProcessingException {
    }
    }

    View Slide

  73. public class FactoryGroupedClasses {
    public void add(FactoryAnnotatedClass toInsert) throws ProcessingException {
    FactoryAnnotatedClass existing = itemsMap.get(toInsert.getId());
    }
    }

    View Slide

  74. public class FactoryGroupedClasses {
    public void add(FactoryAnnotatedClass toInsert) throws ProcessingException {
    FactoryAnnotatedClass existing = itemsMap.get(toInsert.getId());
    if (existing != null) {
    throw new ProcessingException(toInsert.getTypeElement(),
    "Conflict: Class with same id found“ );
    }
    }
    }

    View Slide

  75. public class FactoryGroupedClasses {
    public void add(FactoryAnnotatedClass toInsert) throws ProcessingException {
    FactoryAnnotatedClass existing = itemsMap.get(toInsert.getId());
    if (existing != null) {
    throw new ProcessingException(toInsert.getTypeElement(),
    "Conflict: Class with same id found“ );
    }
    itemsMap.put(toInsert.getId(), toInsert);
    }
    }

    View Slide

  76. FactoryProcessor
    Map
    FactoryGroupedClasses
    Map
    FactoryAnnotatedClass
    String id

    View Slide

  77. public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    }
    } catch (ProcessingException e) { ... }
    }

    View Slide

  78. public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    FactoryAnnotatedClass annotatedClass =
    new FactoryAnnotatedClass( annotatedElement );
    }
    } catch (ProcessingException e) { ... }
    }

    View Slide

  79. public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    FactoryAnnotatedClass annotatedClass =
    new FactoryAnnotatedClass( annotatedElement );
    FactoryGroupedClasses factoryClass =
    getOrCreateFactoryClass( annotatedClass.getQualifiedGroupName() );
    }
    } catch (ProcessingException e) { ... }
    }

    View Slide

  80. public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) {
    FactoryAnnotatedClass annotatedClass =
    new FactoryAnnotatedClass( annotatedElement );
    FactoryGroupedClasses factoryClass =
    getOrCreateFactoryClass( annotatedClass.getQualifiedGroupName());
    factoryClass.add(annotatedClass);
    }
    } catch (ProcessingException e) { ... }
    }

    View Slide

  81. Pizza Store Factory
    1. Only classes can be @Factory
    2. @Factory classes must have empty constructor
    3. Same types are grouped to one factory
    4. id is String and must be unique in factory group
    5. @Factory classes must inherit from specified type

    View Slide

  82. public void checkInheritance(FactoryAnnotatedClass ac) throws ProcessingException {
    TypeElement currentClass = ac.getTypeElement();
    }

    View Slide

  83. public void checkInheritance(FactoryAnnotatedClass ac) throws ProcessingException {
    TypeElement currentClass = ac.getTypeElement();
    while (true) {
    TypeMirror superClassType = currentClass.getSuperclass();
    currentClass = (TypeElement) typeUtils.asElement(superClassType);
    }
    }

    View Slide

  84. public void checkInheritance(FactoryAnnotatedClass ac) throws ProcessingException {
    TypeElement currentClass = ac.getTypeElement();
    while (true) {
    TypeMirror superClassType = currentClass.getSuperclass();
    if (superClassType.getKind() == TypeKind.NONE)
    throw new ProcessingException( classElement, "Class doesn’t inherit Meal” );
    currentClass = (TypeElement) typeUtils.asElement(superClassType);
    }
    }

    View Slide

  85. public void checkInheritance(FactoryAnnotatedClass ac) throws ProcessingException {
    TypeElement currentClass = ac.getTypeElement();
    while (true) {
    TypeMirror superClassType = currentClass.getSuperclass();
    if (superClassType.getKind() == TypeKind.NONE)
    throw new ProcessingException( classElement, "Class doesn’t inherit Meal” );
    if (superClassType.toString().equals( ac.getQualifiedFactoryGroupName() ) )
    return;
    currentClass = (TypeElement) typeUtils.asElement(superClassType);
    }
    }

    View Slide

  86. Code generation

    View Slide

  87. private Map factoryClasses;
    public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    ...
    } catch (ProcessingException e) { ... }
    for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
    factoryClass.generateCode(elementUtils, filer);
    }
    }

    View Slide

  88. public void generateCode(Elements elementUtils, Filer filer) throws IOException {
    MethodSpec.Builder method = MethodSpec.methodBuilder("create")
    .addModifiers(Modifier.PUBLIC)
    .addParameter(String.class, "id")
    .returns(TypeName.get(superClassName.asType()));
    }

    View Slide

  89. public Meal create(String id) {
    }

    View Slide

  90. public void generateCode(Elements elementUtils, Filer filer) throws IOException {
    MethodSpec.Builder method = MethodSpec.methodBuilder("create")
    ...
    for (FactoryAnnotatedClass item : itemsMap.values()) {
    method
    .beginControlFlow("if ($S.equals(id))", item.getId())
    .addStatement("return new $L()",
    item.getTypeElement().getQualifiedName().toString())
    .endControlFlow();
    }
    }

    View Slide

  91. public Meal create(String id) {
    if ("Margherita".equals(id))
    return new MargheritaPizza();
    if ("Calzone".equals(id))
    return new CalzonePizza();
    if ("Tiramisu".equals(id))
    return new Tiramisu();
    }

    View Slide

  92. public void generateCode(Elements elementUtils, Filer filer) throws IOException {
    MethodSpec.Builder method = MethodSpec.methodBuilder("create")
    ...
    method.addStatement(
    "throw new IllegalArgumentException($S + id)",
    "Unknown id = "
    );
    }

    View Slide

  93. public Meal create(String id) {
    if ("Margherita".equals(id))
    return new MargheritaPizza();
    if ("Calzone".equals(id))
    return new CalzonePizza();
    if ("Tiramisu".equals(id))
    return new Tiramisu();
    throw new IllegalArgumentException( "Unknown id = ” +id );
    }

    View Slide

  94. public void generateCode(Elements elementUtils, Filer filer) throws IOException {
    MethodSpec.Builder method = MethodSpec.methodBuilder("create")
    ...
    TypeSpec typeSpec =
    TypeSpec.classBuilder( factoryClassName )
    .addMethod( method.build() ).build();
    // Write MealFactory.java
    JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
    }

    View Slide

  95. public class MealFactory {
    public Meal create(String id) {
    if ("Margherita".equals(id))
    return new MargheritaPizza();
    if ("Calzone".equals(id))
    return new CalzonePizza();
    if ("Tiramisu".equals(id))
    return new Tiramisu();
    throw new IllegalArgumentException( "Unknown id = ” +id );
    }
    }

    View Slide

  96. Attempt to recreate a file for type
    com.example.MealFactory

    View Slide

  97. Processing Rounds
    Round Input Output
    1. PizzaStore.java
    Meal.java
    CalzonePizza.java
    Tiramisu.java
    Magerita.java
    MealFactory.java

    View Slide

  98. Processing Rounds
    Round Input Output
    1. PizzaStore.java
    Meal.java
    CalzonePizza.java
    Tiramisu.java
    Magerita.java
    MealFactory.java
    2. MealFactory.java -- none --

    View Slide

  99. Processing Rounds
    Round Input Output
    1. PizzaStore.java
    Meal.java
    CalzonePizza.java
    Tiramisu.java
    Magerita.java
    MealFactory.java
    2. MealFactory.java -- none --
    3. -- none -- -- none --

    View Slide

  100. public class FactoryProcessor extends AbstractProcessor {
    private Map factoryClasses;
    public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    // scan annotations
    } catch (ProcessingException e) { ... }
    for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
    factoryClass.generateCode(elementUtils, filer);
    }
    }
    }

    View Slide

  101. public class FactoryProcessor extends AbstractProcessor {
    private Map factoryClasses;
    public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    // scan annotations
    } catch (ProcessingException e) { ... }
    for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
    factoryClass.generateCode(elementUtils, filer);
    }
    }
    }

    View Slide

  102. public class FactoryProcessor extends AbstractProcessor {
    private Map factoryClasses;
    public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    // scan annotations
    } catch (ProcessingException e) { ... }
    for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
    factoryClass.generateCode(elementUtils, filer);
    }
    }
    }

    View Slide

  103. public class FactoryProcessor extends AbstractProcessor {
    private Map factoryClasses;
    public boolean process(Set<> annotations, RoundEnvironment rv) {
    try {
    // scan annotations
    } catch (ProcessingException e) { ... }
    for (FactoryGroupedClasses factoryClass : factoryClasses.values()) {
    factoryClass.generateCode(elementUtils, filer);
    }
    factoryClasses.clear();
    }
    }

    View Slide

  104. Instantiation

    View Slide

  105. public class PizzaStore {
    private MealFactory factory = new MealFactory();
    public static void main(String[] args) throws IOException {
    PizzaStore pizzaStore = new PizzaStore();
    Meal meal = pizzaStore.order( readConsole() );
    System.out.println("Bill: $" + meal.getPrice());
    }
    public Meal order(String mealName) {
    return factory.create(mealName);
    }
    }

    View Slide

  106. View Slide

  107. Instantiation using Reflections
    ButterKnife.inject(Activity);

    View Slide

  108. Instantiation using Reflections
    ButterKnife.inject(Activity);
    try {
    Class> injector = Class.forName(activityClassName + "$$ViewInjector");
    } catch (ClassNotFoundException e) { ... }

    View Slide

  109. We are hiring ;-)
    http://hannesdorfmann.com
    https://github.com/sockeqwe/annotationprocessing101

    View Slide