Weapons for Boilerplate Destruction [Pt.1]— Intro
This is the first of a series of short posts about code generation. I’ll try to explain the basics and then I’ll show you some real world examples from a library I personally wrote. So with that said, let’s begin!
First of all, why should you be interested? Well, many libraries that you use generate code. Probably the most famous examples are ButterKnife, Dagger2 or Realm. Why do they use it? Some libraries, such as ButterKnife, generate code specifically to spare you the repetitive work. Others such as Dagger2 do it because the functionality they want to give you requires code that depends on your specific usage. And if you had to write it all by yourself, you probably wouldn’t use it! Then there are libraries such as Realm, that besides other things, alter your code (we’ll look into that in some of the future posts ;) ).
So, it’s time to unveil the mystery behind these libraries.
How is it done? There are 2 ways:
- Annotation processing
- Bytecode weaving
Both methods generate code during build time (as shown in the build process below), but each has it’s own advantages and disadvantages.
This is a tool built in javac for scanning and processing annotations. You can register your own annotation processor for certain annotations. (In case you don’t know what an annotation is - check out official doc) Annotation processing has been available since Java 5 but a usable API has been available since Java 6. It allows you to generate new .java files, but not to modify existing ones. Because it generates .java files, it can also be debugged and is easy to test.
This method allows you to inspect, create new or modify existing .class files before they are compiled to dex file. Because it manipulates bytecode, it cannot be debugged and is hard to test. Since Gradle plugin 1.5.0-beta1 we have a more simple API for achieving this in the Android world, called Transform API.
With the library Blade I wanted to destroy as much boilerplate/repetitive code as possible, so that it is easy to use while allowing the user to debug the code as if it was his own (not some black box). How did I do that? I simply combined them — an annotation processor generates new code that is then called by weaved bytecode.
In the next post I’ll show you how to create your own annotation processor. It’s easier than you would think ;)
See you later.