Mixins for Java

Just for fun, I decided to implement mixins for Java, then record a screencast demonstrating how it works whilst at the same time trying to say “Um” as little as possible (I succeeded on the first two counts and failed on the last).

Introduction

Click here to skip to the screencast

A Mixin is a bit of re-usable code that can be mixed in to classes. It is a form of code re-use more flexible than inheritance and less dangerous than multiple-inheritance. It works like this:

// this is a mixin: an interface labeled with a default implementation
@MixinType(implementation = HelloMessageBearer.class)
interface MessageBearer {
    public String getMessage();
}
 
// this is a mixin implementation. Classes implementing MyMixin will
// automatically have this implementation code mixed into them
class HelloMessageBearer implements MessageBearer {
    public String getMessage() { return "Hello World!"; }
}
 
// this is a class that uses the MessageBearer mixin
@MixinBase
abstract class MessagePrinter implements MessageBearer {
    public void printMessage() {
        // code can refer to methods of the mixin interface
        System.out.println("message: " + this.getMessage());
    }
}
 
// Demo code. Because this is a runtime library,
// "MixinSupport.create(X.class)" must be used instead of "new X()"
MessagePrinter printer = MixinSupport.create(MessagePrinter.class);
printer.printMessage(); // outputs "message: Hello World!"

Mixins for Java is a pure-Java runtime library that implements mixins in a type-safe way without requiring either a preprocessor (as JAMIE does) or whole new compiler (as AspectJ does).

All code is BSD licensed and available at The Mixins for Java Github repo.

Screencast

The screencast requires flash and sound. You should click the “full screen” icon in the bottom right for best quality. If you don’t have the flash player, you can download the mp4 file here.

16 thoughts on “Mixins for Java”

  1. 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.

  2. 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.

  3. 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.

  4. > 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)

  5. 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

  6. 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.

  7. 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

  8. 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)

  9. Hi bernie,

    I like. :)
    Very easy to use, no config hassle, nicely done.

    One thing though: Inheritance only works when the class derived from a MixinBase again declares the MixinType interface. Which is commonly accepted as bad practice.
    It should be possible to detect MixinType interfaces in the type hierarchy… ?

    And why is MixinSupport a singleton vs. utility class with static methods?

    cheers,
    Bno.

    1. > Inheritance only works when the class derived from a MixinBase again declares the MixinType interface.

      Not true – I think you’re confusing annotations and interfaces. In any case, the two classes don’t extend each other, they are composed together.

      > And why is MixinSupport a singleton vs. utility class with static methods?

      Good question. The reason was that MixinSupport declares an interface (MixinEngine if I recall correctly), so by making it an object, client code can accept any object of type MixinEngine and avoid tying themselves to a specific implementation of the mixin concept. That’s a pretty unlikely use case, but it seemed like a good idea at the time :o)

      1. Hi Bernie,
        thanks for your comments.
        Sorry, misunderstanding.

        I was talking about a class extending a class implementing a mixin.

        Like so:

        @MixinType(implementation=LoggerMixIn.class)
        public interface Logging {
            Logger getLogger();
        }
         
        @MixinBase
        public abstract class LoggingClass implements Logging {
            public void doSomething() {
                getLogger().debug("Hello World!");
            }
        }
         
        @MixinBase
        public abstract class AnotherLoggingClass extends LoggingClass {
         
            @Override
            public void doSomething() {
                getLogger().debug("Hello World from another one!");
            } 
        }

        Code generation will fail unless AnotherLoggingClass also implements the Logging interface.

        Is there another way to solve this?

        Chers,
        Bno

        1. Aah, I see.

          Casting my mind back to the implementation, I see no reason why this issue couldn’t be solved by putting an @Inherited annotation in MixinBase.java. It’s just that annotations aren’t inherited by default, and I never thought about this use case.

          That said, since I never tested with inheritance, I can’t guarantee that there’s no part of the system that would break if you just added the @Inherited annotation.

          If you try it, let me know!

          Bernie :o)

  10. Hi Bernie,

    still not what I meant but another good point :)
    I tried it and it works, @MixinBase can be omitted in extending classes.

    What I meant though was I don’t want to repeat “implements Logging” in each derived class. So I tried something else and it works like a charm:

    Implemented getInterfaces() in MixinFactory.

    	public static List<Class> getInterfaces(final Class clazz) {
    	    Class c = clazz;
    	    List<Class> result = new ArrayList<Class>(Arrays.asList(c.getInterfaces()));
    	    while (true) {
    	        c = c.getSuperclass();
    	        if (c == null) {
    	            break;
    	        }
    	        result.addAll(Arrays.asList(c.getInterfaces()));
    	    }
     
    	    return result;
    	}

    called it in line 90:

        for (Class mixinType: getInterfaces(mixinBase)) {
            ....
        }

    This does the trick, the “Logging” interface need not be declared again in AnotherLoggingClass.

    Cheers,
    bno

Comments are closed.