We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Annotation Processing 101

00:00

Formal Metadata

Title
Annotation Processing 101
Title of Series
Number of Parts
46
Author
License
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
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.
Process (computing)Thermodynamischer ProzessPositional notationSoftware developerAndroid (robot)Standard deviationProcess (computing)Physical systemJava appletPlug-in (computing)Lecture/Conference
Process (computing)MereologySource codeJava appletComputer fileCodeBoilerplate (text)Process (computing)Electric generatorMereologyCodeJava appletMobile appProgramming languageWritingBitBoilerplate (text)Different (Kate Ryan album)Thermodynamischer ProzessInstance (computer science)State of matterMachine codeFiber bundleWorkstation <Musikinstrument>Computer animationLecture/Conference
View (database)String (computer science)Similarity (geometry)Water vaporEndliche ModelltheorieState of matterParameter (computer programming)Social classCovering spaceComputer fileInstance (computer science)Computer-assisted translationType theoryView (database)Fiber bundleCodeComputer animation
CodeType theoryKeyboard shortcutView (database)Social classInterface (computing)Computer fileCASE <Informatik>Multiplication signMusical ensembleComputer animation
Bit error rateCodeJava appletReflection (mathematics)Java appletCodeRun time (program lifecycle phase)CompilerThermodynamischer ProzessProcess (computing)Bit rateMultiplication signComputer animationLecture/Conference
Code generationCompilerCodeJava appletSquare numberSource codeCartesian coordinate systemView (database)Process (computing)Thermodynamischer ProzessVirtual machineCodeMachine codeJava appletBuildingServer (computing)Local ringLine (geometry)BenchmarkReflection (mathematics)Right angleLecture/ConferenceComputer animationProgram flowchart
Square numberReflection (mathematics)BenchmarkUniform resource locatorParsingInjektivitätWorkstation <Musikinstrument>View (database)Square numberIndependence (probability theory)Thermodynamischer Prozess2 (number)Software frameworkLecture/ConferenceProgram flowchart
GoogolScale (map)BefehlsprozessorThermodynamischer ProzessInjektivitätSquare numberSoftware frameworkRun time (program lifecycle phase)BefehlsprozessorGraph (mathematics)BitScaling (geometry)Inheritance (object-oriented programming)Object-oriented programmingReflection (mathematics)Multiplication signCASE <Informatik>GoogolFocus (optics)Computer animationLecture/Conference
GoogolScale (map)BefehlsprozessorEvent horizonRobotReflection (mathematics)Event horizonRevision controlThermodynamischer Prozess2 (number)Bus (computing)Social classImage registrationBitBenchmarkPoint (geometry)Limit (category theory)Computer fileGoodness of fitComputer animation
Computer fileCodeAbstract state machinesAbstract syntax treeComputer fileProjective planeLimit (category theory)Social classBytecodeCodeNetwork topologyStatement (computer science)Computer animation
CodeJava appletStatement (computer science)Abstract syntax treeWritingThermodynamischer ProzessDebuggerBytecodeSoftware bugSquare numberCycle (graph theory)Library (computing)AuthorizationMobile appCASE <Informatik>Roundness (object)Multiplication signBuildingLecture/ConferenceMeeting/Interview
CASE <Informatik>Sampling (statistics)Thermodynamischer ProzessJava appletData storage deviceInterface (computing)Point (geometry)Cartesian coordinate systemLecture/ConferenceJSON
Physical systemFactory (trading post)Equals signString (computer science)Social classData typeTap (transformer)Instance (computer science)Social classTheoryField (computer science)Factory (trading post)Process (computing)Type theoryGroup actionAbstractionPurchasingString (computer science)Pattern languageExterior algebraThermodynamischer ProzessCartesian coordinate systemArithmetic meanCodeComputer animation
Factory (trading post)Social classConstructor (object-oriented programming)Type theoryString (computer science)Local GroupUniqueness quantificationSocial classFactory (trading post)Type theoryTape driveUniqueness quantificationElectronic mailing listGroup actionRule of inferenceWordThermodynamischer ProzessConstructor (object-oriented programming)2 (number)Computer animation
Factory (trading post)Mealy machineElement (mathematics)Type theoryError messageMessage passingOnline helpIntegrated development environmentComputer fileString (computer science)Thermodynamischer ProzessOrbitPositional notationSet (mathematics)Social classElement (mathematics)Type theoryConstructor (object-oriented programming)Utility softwareComputer animation
RepetitionThermodynamischer ProzessElement (mathematics)Data typeSocial classCodeString (computer science)Element (mathematics)Thermodynamischer ProzessType theoryConstructor (object-oriented programming)Java appletVariable (mathematics)Abstract syntax treePrimitive (album)Factory (trading post)Parameter (computer programming)Integrated development environmentRoundness (object)Different (Kate Ryan album)Set (mathematics)RootElectronic mailing listNetwork topologyAbstractionTape driveBarrelled spaceProcess (computing)RoutingMetropolitan area networkComputer animation
Data typeElement (mathematics)Thermodynamischer ProzessMetadataSocial classElement (mathematics)InformationInheritance (object-oriented programming)Instance (computer science)Type theoryMetreComputer animation
Meta elementInfinityService (economics)Process (computing)Thermodynamischer ProzessComputer fileTouch typingSocial classFactory (trading post)Service (economics)Mobile appInformationJava appletMeta elementComputer animation
RepetitionMeta elementInfinityService (economics)Port scannerJava appletThermodynamischer ProzessComputer filePositional notationService (economics)GoogolCartesian coordinate systemProcess (computing)Lecture/ConferenceComputer animationJSON
InjektivitätCartesian coordinate systemService (economics)Computer fileThermodynamischer ProzessJava appletInformationMeta elementIndependence (probability theory)Lecture/Conference
Factory (trading post)Social classElement (mathematics)Thermodynamischer ProzessFactory (trading post)Right angleRule of inferenceBit rateSocial classElement (mathematics)DivisorType theoryError messageInformationInstance (computer science)Thermodynamischer ProzessMessage passingInterface (computing)Telephone number mappingMassComputer animation
Element (mathematics)Thermodynamischer ProzessError messageAndroid (robot)IRIS-TPosition operatorLine (geometry)Cursor (computers)Computer fileProjective planeStatement (computer science)Factory (trading post)Interface (computing)Workstation <Musikinstrument>Lecture/ConferenceMeeting/InterviewComputer animation
Java appletThermodynamischer ProzessMaizeIntegrated development environmentElement (mathematics)DivisorMultiplication signTracing (software)Crash (computing)Thermodynamischer ProzessJava appletCartesian coordinate systemSource codePointer (computer programming)Computer programError messageException handlingPoint (geometry)Process (computing)Arrow of timeComputer animation
Element (mathematics)Thermodynamischer ProzessError messageError messageLetterpress printingLoop (music)Exception handlingThermodynamischer ProzessBitPoint (geometry)Block (periodic table)Crash (computing)Instance (computer science)Lecture/ConferenceComputer animation
Line (geometry)Element (mathematics)Object-oriented programmingString (computer science)Exception handlingConstructor (object-oriented programming)Social classFactory (trading post)Exception handlingThermodynamischer ProzessArrow of timeError messageRule of inferenceElement (mathematics)NumberMessage passingSocial classFactory (trading post)Constructor (object-oriented programming)Computer animation
Element (mathematics)MetreElement (mathematics)Field (computer science)Power (physics)Thermodynamischer ProzessConstructor (object-oriented programming)Instance (computer science)Exception handlingBit rateSocial classParameter (computer programming)Variable (mathematics)Bit
Local GroupFactory (trading post)String (computer science)Uniqueness quantificationType theoryType theoryFactory (trading post)Rule of inferenceGroup actionCovering spaceSocial classFocus (optics)DivisorInstance (computer science)Endliche ModelltheorieTape driveData modelDecision tree learningComputer animationLecture/ConferenceMeeting/Interview
Data typeElement (mathematics)String (computer science)Social classFactory (trading post)Social classData modelCASE <Informatik>Source codeFactory (trading post)Type theoryException handlingElement (mathematics)String (computer science)Thermodynamischer ProzessRight angleMereologyProcess (computing)DataflowPresentation of a groupElectronic mailing listPhysical lawTape driveDivisorWorkstation <Musikinstrument>Endliche ModelltheorieFilm editingGroup actionComputer animation
String (computer science)Social classFactory (trading post)Level (video gaming)Hash functionElectronic mailing listExistenceData modelException handlingMathematicsThermodynamischer ProzessMultiplication signGreatest elementComputer animation
String (computer science)Social classFactory (trading post)Greatest elementThermodynamischer ProzessElectronic mailing listTheorySampling (statistics)Lecture/ConferenceComputer animation
Element (mathematics)Social classThermodynamischer ProzessFactory (trading post)Element (mathematics)Factory (trading post)Electronic mailing listException handlingThermodynamischer ProzessSocial classType theorySpacetimeInheritance (object-oriented programming)Physical law
Social classFactory (trading post)Element (mathematics)Electric currentData typeSocial classFactory (trading post)Type theoryInformationInheritance (object-oriented programming)Element (mathematics)Network topologyMetadataIterationBit rateError messageLoop (music)BuildingJSONComputer animation
Data typeElectric currentSocial classElement (mathematics)Inheritance (object-oriented programming)Equals signSocial classFerry CorstenInstance (computer science)Loop (music)Utility softwareInheritance (object-oriented programming)Type theoryElement (mathematics)Rule of inferenceTelephone number mappingInterface (computing)RootString (computer science)Java appletFormal verificationOcean currentProcess (computing)Programming languageObject-oriented programmingLecture/ConferenceJSON
Code generationFactory (trading post)Social classThermodynamischer ProzessSocial classEndliche ModelltheorieFactory (trading post)Source codeCodeException handlingData modelElectric generatorProcess (computing)Sampling (statistics)Lecture/Conference
Statement (computer science)Parameter (computer programming)Java appletType theoryElectric generatorProcess (computing)Source codeSquare numberString (computer science)MetrePower (physics)Natural numberTerm (mathematics)Computer animation
Exception handlingEquals signBit rateString (computer science)Factory (trading post)Statement (computer science)PlanningSocial classArithmetic mean
Equals signException handlingBuildingData typeStatement (computer science)CASE <Informatik>Type theoryComputer fileSource codeComputer animationJSON
Equals signFactory (trading post)Projective planeProcess (computing)Factory (trading post)AuthorizationMachine codeThermodynamischer ProzessRoundness (object)Computer fileJSONLecture/ConferenceComputer animation
Process (computing)RoundingFactory (trading post)Java appletFunction (mathematics)outputRoundness (object)Factory (trading post)outputException handlingFunction (mathematics)Multiplication signInsertion lossProcess (computing)Java appletData storage deviceSource codeComputer animation
Social classFactory (trading post)Instance (computer science)Block (periodic table)Roundness (object)Thermodynamischer ProzessFactory (trading post)Social classMultiplication signElectronic mailing list2 (number)Computer fileData storage deviceCausalityException handlingGroup actionReal numberVideo gameLevel (video gaming)Lecture/Conference
Physical systemMultiplication signWorkstation <Musikinstrument>Social classSampling (statistics)Factory (trading post)Instance (computer science)Process (computing)Data storage deviceMereologyJava appletPositional notationThermodynamischer ProzessCodeSource codeCompilerLecture/ConferenceJSON
DivisorBuildingWindowEmailSample (statistics)Module (mathematics)Social classJava appletCodeReflection (mathematics)Thermodynamischer ProzessSocial classSoftware developerMultiplication signInstance (computer science)Level (video gaming)Computer animation
MaizeFactory (trading post)Sampling (statistics)CodeBlogJSONXMLUMLLecture/Conference
Transcript: English(auto-generated)
Thank you, welcome everybody. I'm Hannes Dofman, I'm an Android developer at Tech Haru and I'm here today to talk about Annotation Processing. Annotation Processing was introduced with Java 5 in September 2004
and the standard was released six months later, so the standard is still the same that we use nowadays. And it is a plug-in system that allows you as a developer to write your own annotation processor and kick them in.
So it's part of the Java C, of the Java compiler, and it's annotation based, so basically it scans for annotations and afterwards you can generate code out of it. And why is this useful for you? Well, we all know that the Java programming language requires
to write a little bit of boilerplate code and writing Android apps is no difference, right? Moreover, you have to write a lot of codes for doing such simple things like saving instance states in a bundle and so on. And here are some quite useful annotation processors like butter knife.
Please raise your hand if you never have heard before of butter knife. Really? Get out of... No, just kidding. No, what is butter knife? Butter knife was made by the almighty Jake Wharton and it basically reduces the amount of writing that all those find view by ID
and cast it to the corresponding class and so on. So all you have to do is inject view and you're ready to go. And there's something similar for saving instance states. So instead of putting all your data by hand into the bundle, all you have to do is annotate it at icicle.
And it also works seamlessly with pulling out the data from the bundle. Pretty the same for fragment arguments. So instead of writing your own bundle and so on and putting all into it, you can use annotations. Parcilla, obviously, I mean, every one of us should have
implemented the Parcilla interface, not only once. And we all know how painful it is or how much code it requires to write. With Parcilla, it's basically the simplest way to make your model class possible.
But there's also something useful like this annotated adapter. So what do you do? You annotate just your view types like doc or cat, specify the layout file and this one generates the view holder classes for you. But not only like that, it also generates an interface you have to implement
called adapter binder, in this case animals adapter binder. And it also reduces the code you have to write for determining or distinguishing between view types. You don't have to implement it on create view type or the bind view type.
All you have to do is implement the interface. The interface defines such a method like that where you get the generated view class holder. And yeah, basically you have to do that for all of your view types. But that's quite cool for you as developers, so you don't have to write it all by your own.
You can use the generated code. But it also brings a lot of performance, or it brings a lot of performance boosts. So how does annotation processing work? As already said, it's Java C, the Java compiler feature,
and it runs on compile time, not at runtime like reflections. So to clarify that, we are not talking about reflections. I'm not evaluating anything on runtime. This is done at compile time. So before you compile all your source codes, annotation processing gets started in a full GVM, in a Java machine.
So basically your annotation processor is like any other Java application. It runs in a full GVM on your local machine or build server or whatever. And it's like native code because it generates code like it would be handwritten. So who of you is not familiar with JSON or Jackson?
Everyone, fine. It's parsing JSON, right? And Jackson and Jason do that by using reflections. So there's a little benchmark from the guys at Blue Line Labs. And they parse 60 items.
You can find the benchmark on this URL. And basically why they do that is because they have written their annotation processing-based JSON parser called Logan Square. And now instead of around 60 to 70 milliseconds to parse 60 items, it only takes 17 milliseconds to serialize 10 milliseconds.
Dagger. Who of you is not familiar with Dagger? It's a dependency injection framework. And there was Dagger 1 implemented by the guys at Square. And it used annotation processing to generate code,
but it also used reflection at runtime to stick the object graph together and to handle some inheritance edge cases. So it used a little bit reflection on runtime. And Google have used it at Google scale for internally dependency injection.
And they saved 13% of CPU time by migrating to Dagger 2. That is a fork of Dagger 1 they have implemented in the last few months. And it's completely without reflection. So zero reflection here, CPU increase or decrease of 13%. Event Bus.
Event Bus was announced today, or at least Event Bus version 3, which is based on annotation processing as well. So with Auto and Event Bus 2.4, you have a performance of about 2,000 to 4,000 registrations.
So this means more means better performance. So Auto can register around 1,500 classes per second. Event Bus 2.4 has this naming convention. It's made up that a little bit more. And you get something about 3,500.
But with Event Bus 3, based on annotation processing, you get around 17,000 per second. This benchmark was done on a Nexus 9. That all sounds good, but there are some limitations.
And one of these limitations is you can generate only new files. So you cannot generate or manipulate any existing files. So you can't use your class you have handwritten and put in some code some kind of magically. That doesn't work. It might work with bytecode manipulation like ASM or Afterburn.
Or with something like Project Lombok does. It uses some internally Java C features, some private features, to manipulate the abstract syntax tree. So it basically adds statements during compiling your code.
So what's the problem with that? It's not debuggable anymore. So with annotation processing, as already said, it's like handwriting code. And you can debug it. You can put your debugger in, you can see what's going on, and so on. With that byte manipulation, etc., you don't have the chance to debug it.
Why is this important? Well, you have to trust the author of this bytecode libraries. I mean, if Square would do that, I would trust them, obviously. But, I don't know, we are building a tickeroo, an app called Kicker, for a lot of sport enthusiasts in Germany.
It's one of the biggest soccer magazines in Europe as well. And we have around 35 million sessions per month. And we are regularly updating in a cycle of four to six weeks. And what is if the library has a bug if it was based on bytecode?
Let's be honest, every library or every piece of code has a bug somewhere. And can we really rely on such a library in that case? If we have such a huge app? Probably, probably not. It really depends, right? So I'm not saying that this is something you shouldn't do or should avoid.
But I'm just saying it's not my cup of tea. So now let's get started. Now we try to step over some edge cases and some problems we will face during writing our own annotation processor. So it's not Android-based, it's a Java sample.
But you should get the point of what it takes to write your own annotation processor. So basically it's a pizza store. So we have an interface meal and our pizza store is selling our pizza margherita which implements meal and a calzone and a dessert at Tiramisu.
So we have a plain old Java application with the main method and we read the console in and if the user types margherita then we instantiate our margherita pizza. If the user types in Tiramisu then he gets a Tiramisu. So basically we have some kind of factory pattern, it's not an abstract pattern,
an abstract factory pattern. So this talk is not about design patterns, it's about annotation processing. And guess what? Instead of writing that by hand we want to generate that kind of code.
So let's get started. First we define our annotation like addFactory. And we say okay, we have a string id which is basically what the user types in which would be margherita for instance or Tiramisu. And then we define a type. The type class is for instance meal, something like that, meal.class.
So everything with the same type gets grouped together in one factory afterwards. So calzone would look like this and Tiramisu is pretty the same. So we also introduce some rules for our factory annotation processor.
The first rule is only classes can be annotated with addFactory. The second rule is the class annotated must have an empty constructor. The same types are grouped to one factory. That was what I mentioned before by saying yeah, they have the type meal.class
and everything with the type gets together in one factory. And the id must be unique. So there cannot be two margheritas or two annotated margheritas. And the factory class must or the addFactory annotated class
must inherit from the specified type. So what does that mean? That means if we have a pizza margherita and we define here meal, then it also must be a meal. So annotation processing. Basically, you have to extend from abstract processor
and then there is the init method like that. So your annotation processor must provide an empty constructor. However, to get initialized, this method gets called and it passes our processing environment. And the processing environment provides some helper and util classes
we have to use like type utils, element utils, a filer to write files and a messenger to write error messages. Okay, the next thing is we have to register our annotation processor for certain annotations.
So we do it by returning or by overwriting getsupported annotations and all we have to do is return a set of strings with full qualified class names. So we use the factory annotation.class.getcanonicalName to register the addFactory annotation to this annotation processor.
The process method then is the method that gets called to process. So what you basically do here is you say round the environment which is rv.getElementsAnnotatedWithFactory.class and now you get a list or a collection
you can iterate over with classes or with elements annotated with addFactory. So what is an element now? Element can basically be everything in Java. So we are scanning now this class but don't see it as Java code.
See it more like some kind of tree. So basically we have a root element which is a kind of type element. Then we have a variable element. Variable element stands for this variable. The same for other.
So there's no difference between primitives or classes. It's a variable element. Then we have an executable element like the constructor. And the method is an executable element as well. This parameter of this method is a type element because it's referring to a type.
And what you can do is you can iterate over this. So the type element, the root element can iterate over its children and it also works the other way around. But what is it basically an element? An element is basically just that simple Java code transferred in something internally used by the abstract syntax tree.
And there's another type. Another thing you have to know, the type mirror. The type mirror basically provides some meta information about that special element. So for instance, we have an element, a type element foo.
OK, we know the full name, we know it's called foo and we know it's a class and we know it's public. But we don't know, for instance, the inheritance hierarchy. And such metadata you get by the type mirror of this element. OK, so those three methods are basically what we are overriding
or have to implement to get started. And how do we register the annotation processor itself to Java C, to the compiler, to the Java compiler? Basically we pack our annotation processor into a jar like any other app or any other Java compatible thing
and pack the classes in there. But we also have to provide this meta info folder which contains a subfolder services which contains a single file called javax.annotation.processing.processor. And in this file we basically write only the class name of factory processor,
the full qualified one. And Java, I'm sorry, Java, or Java C to be precise, scans all the jars in your build path and checks whether there is such a file and reads that file out and registers that annotation processor.
So it's pretty easy. But you have to do that by hand and guess what? There's an annotation processor doing that for you. So it's possible to use annotation processor as well because as already said, an annotation processor
is just a simple Java application like any other. So you can use other annotation processors. There's one out of service from Google which basically generates only this processor file for you and puts it in the meta info folder. But you could also use Dagger and so on for dependency injection if you really are going to build, I don't know, something very huge
that has a lot of dependencies and so on. Okay, so let's start with our five rules. The first one is only classes can be factored at factory annotated. So what does it mean? So we iterate over these elements that are annotated at factory
and as I said before, type element is a class, right? Not. We can do this with instance of because type element means not only a class, it can also be an interface, right? But we want to limit that to a class. So instead we use element kind dot class.
So it's basically an enum and it holds all that kind of information like it's a class, it's a primitive and so on. It's an interface and we use that instead of instance of. So never use instance of in annotation processing. And to write an error lock, we use the messenger which was one of the things we pulled out from the init method.
And basically we can say, okay, it's an error. We could also use a warning or a notice or a lock and write your message and that's also something pretty useful. We can say which element has caused this error.
So in IntelliJ or Android Studio or in any other modern IDE, the IDE is smart enough to recognize this one and if you click on the error message, you will immediately see the line and the file with the cursor on the correct position where the error is.
So for simplicity, let's pull it out in a method like that and call that one. So what if I do something silly like that? So I do that, I set it to null in this if statement
and now I pack it into a jar and put it into my Android project and I use the at factory annotation on our interface. So what will happen? Null pointer, etc. Whoa, what's that? And there's a lot of stack trace and so on and it really took some time to figure out where exactly the problem is
and what was the problem? The problem was that even if you print an error, the program doesn't stop here. It doesn't terminate after the error. So it's still a Java application and if a Java application crashes because of a null pointer exception, the whole JVM crashes, the whole annotation processor crashes,
moreover the whole compile process of compiling all your sources crashes and gives that error and it doesn't print your error anyway because it's crashed. So this one gets lost because of that one. So that's something you have in mind. You have to ensure that the process method doesn't crash.
That can be a little bit tricky. For instance, if you have another method like check other things and we do there something like that and print an error message in there and how do we easily return to this point where we have to say, okay, now I have printed an error
but okay, I wouldn't go further in this loop but yeah, how do I do that? So my recommendation here is to use a try-catch block. So basically surround your process method with a try-catch
or put a try-catch into your process method and now instead of returning true or false or something like that to indicate to the calling method that the check other things has handled an error for a processing exception.
And processing exception is basically just a class like that, contains a message and the element who has caused the error. So rule number two. At factory, classes must have an empty constructor. So what we do is basically we just...
So it's still the... No, sorry. So you get the annotated element as parameter and you iterate over all elements in this class. So if you get bits of Margarita class as element, as parameter we iterate over all elements in this one
like can be variable elements, like fields, methods and so on. And once again, we don't use instance of to check but we use the element kind to check if it's a constructor. And if it's a constructor then we can cast it to executable elements
and we can check for the parameters. And we can also check about the modifiers. And otherwise we throw a processing exception. Next, there are two rules we cover into one because they belong together.
So same types are grouped into one factory. So that was what I said before. The type you specify in the annotation, in the addFactory annotation, groups all together to build one factory that can instantiate all of those classes annotated with addFactory.
And the ID must be unique. So there cannot be Margarita used for pizza Margarita and for Calzone as well. So we introduce a data model type or data model class called factory annotated class. And basically represents just a class annotated with addFactory.
And what we do here is we read the values of the annotation out. So how do we do that? Like that. We simply say, okay, give me the factory annotation and we simply read annotation.ID to get the string value of ID
specified by this annotation. And then we do a simple check. If it's null then we throw a processing exception. Then we come to a special edge case. So what will happen? Oh no, we have two conserias to consider. The addFactory annotation provides this type
where we specify meal.class. But what does it mean to write meal.class? It means that the class is already compiled, right? And as I said before, annotation processing happens before compiling your source code.
So if meal.class is part of a jar that is already compiled, then you can go straight ahead like that. So you say, just give me the type and it's a class and everything is fine. So what we do here is basically we pull out the full qualified name of meal.class. But what if the meal.class is specified in our source set
that we are going to compile after the annotation processor is run? So we can't access meal.class right now because it hasn't compiled yet. So in that case, a mirror type exception gets thrown. And luckily, this exception contains a type mirror and of the type mirror we can get the type element
and of the type element we can get the full qualified class name. So the next thing is factory-grouped-class. It's also just a data model class. It basically holds a list of all factory annotated classes
so basically it holds Margarita pizza class, Calzone class, Diorisu class. But instead of saving them into a list, we save it in a hash map and the reason to do so is that we provide that add method and in this add method, we check if item already with this name exists.
So if there's already a Margarita in the class, we throw an exception. Otherwise, yeah, it's fine, we put it in the map. So basically, to sum it up, it's something like that. From bottom to top, we have the factory annotated class
representing a class annotated with a factory. We have the grouped one which is a list of factory annotated class and the processor itself holds also a list of all factories. Because in our sample, we are only using one factory for meal but in theory, there could be more factories, more different factories.
Okay, so how to do that in code? Well, it's easy. We do a factory annotated class and pass the annotated element and this one will throw a processing exception if the ID is null. Next, we query the list of already known factories
for meal.class and get this one back. And finally, we add it and this one will throw an exception if there's already another element with the same ID or another class annotated with the same ID.
Okay, so last, factory classes must inherit from specified type. So how do we do that? There's something like that, check inheritance. And basically, it's just a loop. So we're iterating or climbing up the inheritance tree of this class.
So what we do is, okay, we take current class and here we use the type mirror because I said before that element itself doesn't contain any metadata about the type. There's the type mirror which provides us the information about the inheritance and so on. And basically, we iterate over that. So here before we, yeah, here before we exit the loop,
we say, okay, let's go one step back by using this type utils to get this superclass element. Okay, and similar to element kind,
there's also something special, a special enum for type mirrors called type kind. So instead of using instance of, we also use the enum. And basically, this type.kind stands, type kind.known stands for java.lang.object which means we have reached the root element
of our inheritance hierarchy. And if it has reached that, then yeah, the class hasn't, is not a meal, hasn't inherited from the meal interface. Okay, otherwise, we simply use two strings
to check if they are equal. So we use the current element name, the full qualified name and the qualified name of meal.class and verify if they are the same. If they are the same, we can say, okay, it's fulfilled, the rule is true, we can go on. So now we did all that kind discussed before
here in this try-catch block and we have basically scanned our source code to get the annotated classes and so on. We put that in a data model like factory annotated class and so on. So now we are ready to generate code out of it.
And that's basically this for loop. So we do, we iterate over all factory classes and our sample is only one factory class for the meal.class and generate code of it. So once again, the guys over at Square did a fantastic job and provided us with a tool called Java Poet.
And with Java Poet, it's some kind of builder to make or to generate Java statements and Java source code. So what we can do is, okay, we want to build a method called create which is public and takes a parameter called ID
and the parameter is of type string. And it returns basically meal.class. And it looks like that. So next we iterate over all this factory annotated classes and we can write some if statements.
So if $s means string literal, and so it's basically just like string.format but in an enhanced way. So it already escapes strings for you. And $l is plain old literal. And you get that.
Last but not least, we cover the case where the ID is unknown and simply add a statement like that. And you will see something like that. And to finish them, we have to specify a type
because that was only the method we was defining. We have to specify the type and use the file from the init method to generate the source file. And we are done. Okay, so now we put that in the jar, compile it to a jar, put it into our Android project
and want to use the addFactory annotated method in some of our Android codes. So what happens? Whoa! Attempt to recreate a file for meal factory. Why does that happen? So the problem is that annotation processing
is done in rounds. In the first round, we have as input all of our Java source code, like pizza store, meal, Java, tiramisu, margarita, and so on. And what comes out is mealFactory.java. In the second round, we take the output from the previous round
because theoretically, in the mealFactory, we have generated an addFactory annotation as well. So we have to scan the mealFactory again for addFactory annotations. But there is none. So we start the third and last round whereas also no input, no output, so we are done.
But what about the problem? So why is this problem or this exception happen? The problem is that factory process get instantiated only once.
However, during the build rounds, the process method gets called several times. So in the first round, we scan our annotations in the dry-catch block and so on. And in the second round, and store it in this factory classes map, which is basically the list of all factory group classes
where the meal.class is in. So in the second round, we do also the scan, but now we have not added anything, but the data is still there from the first round. So in fact, we are recreating here the same file we already have created in the first round.
And that's the problem and the cause of our exception. So a simple workaround is to simply clear the map. But in a real-life annotation process, there might be a smarter way. So instantiation.
If we run our sample pizza store, for the first time, the mealFactory is unknown. So the IDE says, oh, this class, I don't know what class is that. Why? Because, as already said, Java annotation processing is part of Java C.
So it's part of the compiler. And if you haven't compiled your source yet, because it's the first time, the mealFactory is not generated. So, and moreover, it's like I said before, we can't inject byte or the generated code somehow into our existing code.
So we have to instantiate the factory by hand. But now I say, whoa. Oh, sorry. How to do that? Yeah, simply make a rebuild to generate the code to start Java C, which generates the mealFactory. And now I say, but wow, by the knife, how does Jack Wharton is doing? Is there some Jack Wharton magic in there?
Kind of, no, just kidding. Basically, Jack Wharton uses reflection. And what he does is, so if you open the butterknife.inject method, you see something like that. So basically, he uses reflections to get the class,
and afterwards, he instantiated. And now you say, well, but wait, wait, wait. You said, reflection is slow and has some performance issues and so on. Yeah, that's true. So what does Jack Wharton, or what you should do in general? He puts that class into a map.
So only if the activity gets required for the first time, the injector gets instantiated by using reflection. For the second time, it uses a cached one from the map. So here you have to find a compromise between making your annotation processor friendly
for us, other developers, by doing something like that, and performance. Okay, that's it. Last but not least, we are hiring. And the full code of the Spitzer factory sample
can be found at GitHub, and I have also a blog post about it with some more and detailed explanation. So thank you very much for your attention.