Sun 16 Aug 2009
Mixins are a very useful feature of languages like Ruby that help you make small reusable bits of functionality. A few people have tried to implement mixins in Java:
- JAMIE (pdf link) is a preprocessor that adds mixins through automated delegation.
- AspectJ is a whole new language that extends the Java syntax and adds mixins as well as a large number of other features.
- Eclipse adaptors (pdf link) are a different solution to the same problem that mixins address.
Of those three projects, the one I like most is JAMIE, but it modifies the java language and requires a code proprocessor.
Mixins For Java is a library I’ve created that implements mixins through automated delegation using annotations and generics.
I’ve recorded a screencast that shows the library in action:
Click here to view the Mixins for Java screencast
The code is available from code.google.com/p/java-mixins
August 17th, 2009 at 12:32 am
Decent, but, you’ve got 2 fairly major problems here:
1. you need to jump through quite a few hoops to construct instances, and
2. implementation details are leaking through all over the place. Any code overview tool will tell you NamedObject is abstract, when in fact it isn’t intended to be, and any instance of NamedObject that you have, will not return NamedObject when you call its getClass() method. It’ll return MixinSupport$1 or some such.
Have a look at http://projectlombok.org/ - which could solve all these problems and offer mixin support without any implementation details leaking through. Except I was going to call it ‘delegate’ instead of mixin, as it seems a bit closer to the mark. mixin feels more like actually taking the code of the class you want to mix in, whereas delegation is creating a new instance of the mixin, and creating 1 wrapper method for each method in the mixin.
There’s one holdup - due to the way lombok works, it doesn’t at the moment have type info, which it clearly needs to know which method wrappers to build, but that’s something I plan to fix in a future version.
August 17th, 2009 at 8:40 am
Lombok looks very cool.
I had no idea that you could use annotations to replace code in a java file. In fact, I specifically remember reading the white paper for the Javac annotation processing tool, and it said that you are prevented from modifying the same class that the annotations exist on, in order to prevent people from modifying the semantics of the Java language as you have done. Has this restriction been removed, or did you find a clever way around it?
A few specific replies:
> 1. you need to jump through quite a few hoops to construct instances
This is true - I have the cleanest solution I could find that works entirely at runtime, but if you open up the possibility to compile time transfarmation then you get a much cleaner usage. Lombok handles this very nicely.
> 2. implementation details are leaking through all over the place. Any code overview tool will tell you NamedObject is abstract, when in fact it isn’t intended to be.
Yes it is! By using abstract classes, all the semantics of the Java language are preserved. NamedObject is an abstract class, and behaves exactly like one to all tools that analyse its usage. Code that uses NamedObject doesn’t know what non-abstract implementation class is going to be provided, but that’s just like using any abstract class like java.io.InputStream.
In fact, compatibility with existing code analysis tools is one of the major advantages of mixins4j over lombock - any tool can look at NamedObject.java and see what methods it has, because it defines an interface. This is why mixins4j works with IntelliJ and Netbeans out of the box.
August 17th, 2009 at 9:11 am
The restriction has not been removed. Lombok is a hack. The annotation API is all interfaces, but you do not, of course, get an object with an interface type; interfaces aren’t instantiable. For example, under javac, the ProcessingEnvironment you get is actually instanceof JavacProcessingEnvironment. Cast it, and all of a sudden you get a bunch of new methods. Keep digging away and you’ll end up at the core AST, which you can modify in place.
In eclipse, we instrument the parser (Parser.java, which is an enormous source file) to call into lombok core immediately after the parser is done building an AST. The AST is then modified, and returned. That way, all tasks in eclipse, including code analysis, syntax highlighting, outline view, compiling, error reporting, and showing auto-complete dialogs (and even resolving ‘jump to implementation’ requests) all ‘just work’. It’s not standard API, of course, which is why it won’t fly on netbeans and IDEA.
That’s certainly an announce, but lombok can do quite a lot - it can almost turn java into an entirely new language if you want to take it that far. It’s not actually limited to annotations. You could rewrite any use of ‘a == b’ to ‘a == null ? b == null : a.equals(b)’ if you felt that was a good idea, for example.
The source is available (click the ‘fork me on github!’ thingie, or just click contribute), and you can rather easily roll your own additions; lombok will discover handlers via the SPI framework, so you can roll your own additions, stuff them in a jar file, and just make sure both jars are listed when you compile your code with javac, or with eclipse, add it to the classpath in your eclipse.ini that the lombok installer placed there. Your additions will automatically be discovered and used.
August 17th, 2009 at 11:35 am
> Cast it, and all of a sudden you get a
> bunch of new methods. Keep digging away
> and you’ll end up at the core AST, which
> you can modify in place.
That’s beautiful. Why didn’t I think of that! It reminds me of the hack to use reflection to access the singleton sun.misc.Unsafe instance and throw unchecked exceptions. I love abusing undocumented APIs :o)
August 26th, 2009 at 7:35 am
This is quiet neat though I think the AspectJ approach is better in that you are not forcing Abstract and you are more flexible in the mix both interms of what to mix and what the mixin implementor is I just wish it was easier todo this kind of thing and closures in Java, period. Starts to feel like the slow walk to Scala but that lacks on the opensource side (Spring etc) , so I would still like to see better support in Java for these idioms
March 4th, 2010 at 4:12 pm
Hi Bernie,
I’m going to use your mixins library and I was wondering if you are considering deploying it as a Maven2 artifact somewhere, to simplify adoption.
Regards,
Alessio.
March 22nd, 2010 at 4:47 am
Hi Bernie,
I’ve been looking at all the alternatives for mixins, and I like yours the best. It’s friggin’ awesome!
A question and a suggestion follow.
Question: How did were you able to step into the generated code? I wasn’t able to in eclipse.
Suggestion: I don’t think you really need to have the MixinBase annotation _required_. You use it to get the mixer, but you can use a default for that.
Thanks for the awesome library,
Michael
March 22nd, 2010 at 10:38 pm
Hi Michael, thanks for the comment.
I was able to step into the generated code in eclipse because I had set mixins4j to output code to a local folder, then set that folder as a source attachment when eclipse couldn’t find the code the firs time I tried to step in. By default, all code is generated in memory, If I recall correctly there’s a system property that sets a local folder to write files to. Search the source for uses of System.getProperty and you should find the property I used.
You’re right, the @MixinBase could be optional. I made it required because I thought that if I didn’t have a predictable marker on mixin base classes, then the only way of finding the mixins in your project would be to search for uses of MixinSupport (doesn’t work if you instantiate the mixins through reflection, or if you haven’t used a mixin class yet) or searching for classes that implement at least one interface that is annotated @MixinType (doesn’t work if you use a different ImplementationSource that gets implementations from somewhere other than Java interfaces). It’s a matter of taste really, rather than anything scientific.
Bernie :o)
April 17th, 2010 at 8:55 pm
I have never need anything more that java? Why would I?