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

  2. Annotation Processing • Java 5 (Sep. 2004) • JSR 269:

    6 months after Java 5 released • Plugin system for Annotation Processors
  3. Annotation Processing • Part of javac • @Annotation based •

    Generates java source code files
  4. Reduces writing boilerplate code

  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/
  6. public class MyActivity extends Activity { @Icicle String username; @Icicle

    int counter; } Icepick https://github.com/frankiesardo/icepick
  7. public class ItemFragment extends Fragment { @Arg long itemId; @Arg

    int categoryId; } FragmentArgs https://github.com/sockeqwe/fragmentargs
  8. @Parcel public class Person { String firstname; String lastname; int

    age; } Parceler http://parceler.org
  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
  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
  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
  12. Performance

  13. Performance • Compile time code generation • Full JVM •

    “Native” java code (as hand-written) • No reflections!
  14. LoganSquare https://github.com/bluelinelabs/LoganSquare Parsing 60 items 68 ms Gson Jackson LoganSquare

    76 ms Serializing 60 items 74 ms 60 ms
  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
  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)
  17. Otto 1.3.7 https://github.com/greenrobot/EventBus EventBus 2.4 2000 4000 18000 16000 8000

    12000 EventBus 3.0
  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
  19. Limitation • Can generate only new files • Can’t manipulate

    already existing files ◦ Bytecode manipulation (ASM, Afterburn) ◦ AST manipulation (Project Lombok)
  20. Not debuggable!

  21. Example Pizza Store

  22. public interface Meal { public float getPrice(); } public class

    MargheritaPizza implements Meal { @Override public float getPrice() { return 6f; } }
  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; } }
  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); } }
  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”); } }
  26. @Target(ElementType.TYPE) public @interface Factory { String id(); Class type(); }

  27. @Factory( id = "Margherita", type = Meal.class ) public class

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

    CalzonePizza implements Meal { @Override public float getPrice() { return 8.5f; } }
  29. Pizza Store Factory 1. Only classes can be @Factory

  30. Pizza Store Factory 1. Only classes can be @Factory 2.

    @Factory classes must have empty constructor
  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
  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
  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
  34. @Factory( id = "Margherita", type = Meal.class ) public class

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

    MargheritaPizza implements Meal { @Override public float getPrice() { return 6f; } }
  36. public class FactoryProcessor extends AbstractProcessor { private Types typeUtils; private

    Elements elementUtils; private Filer filer; private Messager messager; private Map<String, FactoryGroupedClasses> factoryClasses; @Override public synchronized void init(ProcessingEnvironment processingEnv) { typeUtils = processingEnv.getTypeUtils(); elementUtils = processingEnv.getElementUtils(); filer = processingEnv.getFiler(); messager = processingEnv.getMessager(); }
  37. public class FactoryProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()

    { Set<String> annotations = new LinkedHashSet<String>(); annotations.add(Factory.class.getCanonicalName()); return annotations; }
  38. public class FactoryProcessor extends AbstractProcessor { @Override public boolean process(Set<?

    extends TypeElement> annotations, RoundEnvironment rv) { for (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) { } } }
  39. @Example public class Foo { private int a; private Other

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

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

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

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

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

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

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

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

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

    other; public Foo () { ... } public void setA ( int newA ) { ... } } TypeMirror
  49. public class FactoryProcessor extends AbstractProcessor { @Override public synchronized void

    init(ProcessingEnvironment processingEnv); @Override public Set<String> getSupportedAnnotationTypes(); @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment rv); }
  50. FactoryProcessor.jar - com - example - FactoryProcessor.class - META-INF -

    services - javax.annotation.processing.Processor
  51. @AutoService(Processor.class) public class FactoryProcessor extends AbstractProcessor { @Override public synchronized

    void init(ProcessingEnvironment processingEnv); @Override public Set<String> getSupportedAnnotationTypes(); @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment rv); }
  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
  53. public boolean process(Set<> annotations, RoundEnvironment rv) { for (Element annotatedElement

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

    : rv.getElementsAnnotatedWith(Factory.class)) { if (! (annotatedElement instanceof TypeElement) ){ ... } } }
  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 ); } } }
  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); }
  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(); } }
  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
  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(); } }
  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) { ... }
  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()); } }
  62. public void checkOtherThings(Element element) throws ProcessingException

  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; } }
  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
  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"); }
  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
  67. public class FactoryAnnotatedClass { TypeElement annotatedClassElement; String id; String qualifiedGroupClassName;

    public FactoryAnnotatedClass(TypeElement element) throws ProcessingException { this.annotatedClassElement = classElement; } }
  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!); } }
  69. public FactoryAnnotatedClass(TypeElement element) throws ProcessingException { Factory annotation = classElement.getAnnotation(Factory.class);

    try { Class<?> clazz = annotation.type(); qualifiedGroupClassName = clazz.getCanonicalName(); } catch (MirroredTypeException mte) { } }
  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(); } }
  71. public class FactoryGroupedClasses { String qualifiedClassName; Map<String, FactoryAnnotatedClass> itemsMap =

    new LinkedHashMap<String, FactoryAnnotatedClass>(); public FactoryGroupedClasses(String qualifiedClassName) { this.qualifiedClassName = qualifiedClassName; } }
  72. public class FactoryGroupedClasses { public void add(FactoryAnnotatedClass toInsert) throws ProcessingException

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

    { FactoryAnnotatedClass existing = itemsMap.get(toInsert.getId()); } }
  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“ ); } } }
  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); } }
  76. FactoryProcessor Map<String,FactoryGroupedClasses> FactoryGroupedClasses Map<String,FactoryAnnotatedClass> FactoryAnnotatedClass String id

  77. public boolean process(Set<> annotations, RoundEnvironment rv) { try { for

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

    (Element annotatedElement : rv.getElementsAnnotatedWith(Factory.class)) { FactoryAnnotatedClass annotatedClass = new FactoryAnnotatedClass( annotatedElement ); } } catch (ProcessingException e) { ... } }
  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) { ... } }
  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) { ... } }
  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
  82. public void checkInheritance(FactoryAnnotatedClass ac) throws ProcessingException { TypeElement currentClass =

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

    ac.getTypeElement(); while (true) { TypeMirror superClassType = currentClass.getSuperclass(); currentClass = (TypeElement) typeUtils.asElement(superClassType); } }
  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); } }
  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); } }
  86. Code generation

  87. private Map<String, FactoryGroupedClasses> factoryClasses; public boolean process(Set<> annotations, RoundEnvironment rv)

    { try { ... } catch (ProcessingException e) { ... } for (FactoryGroupedClasses factoryClass : factoryClasses.values()) { factoryClass.generateCode(elementUtils, filer); } }
  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())); }
  89. public Meal create(String id) { }

  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(); } }
  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(); }
  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 = " ); }
  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 ); }
  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); }
  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 ); } }
  96. Attempt to recreate a file for type com.example.MealFactory

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

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

    Magerita.java MealFactory.java 2. MealFactory.java -- none --
  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 --
  100. public class FactoryProcessor extends AbstractProcessor { private Map<String, FactoryGroupedClasses> factoryClasses;

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

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

    public boolean process(Set<> annotations, RoundEnvironment rv) { try { // scan annotations } catch (ProcessingException e) { ... } for (FactoryGroupedClasses factoryClass : factoryClasses.values()) { factoryClass.generateCode(elementUtils, filer); } } }
  103. public class FactoryProcessor extends AbstractProcessor { private Map<String, FactoryGroupedClasses> 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(); } }
  104. Instantiation

  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); } }
  106. None
  107. Instantiation using Reflections ButterKnife.inject(Activity);

  108. Instantiation using Reflections ButterKnife.inject(Activity); try { Class<?> injector = Class.forName(activityClassName

    + "$$ViewInjector"); } catch (ClassNotFoundException e) { ... }
  109. We are hiring ;-) http://hannesdorfmann.com https://github.com/sockeqwe/annotationprocessing101