Lombok and Mixins

274 views
Skip to first unread message

Mark Derricutt

unread,
Aug 26, 2009, 5:50:07 PM8/26/09
to java...@googlegroups.com
After Project Lombok was first mentioned here I was thinking how this could work with mixins, and again after listening about the mixins for java (hereon known as M4J) project mentioned on the last show, and watching the video at http://www.berniecode.com/writing/java-mixins/ I was thinking about this again.

If one was to do mixins lombok style, what would be  good way of doing it?  M4J requires shipping the M4J library/api which you use to construct the mixedin object.  I was thinking lombok style could inject a static method which you use for the constructor, something like MyBusinessObject.newMixin() or the like.

I could really do with using something like this :)

--
Pull me down under...

Reinier Zwitserloot

unread,
Aug 26, 2009, 7:17:22 PM8/26/09
to The Java Posse
Simple.

public class Something extends Whatever implements List {
private @Delegate List listDelegate = new ArrayList();
}

lombok would add all methods that exist in List.java (as that is the
type of 'listDelegate' - arraylist is merely what's assigned to it,
it's about the type of the field you stick @Delegate on), and
implement each one simply by wrapping a call through to the
listDelegate object. e.g:

public int size() {
return listDelegate.size();
}


We're going to add this, because it's cool, simple, and yet very
powerful, but we need to extend lombok a little further first - right
now lombok does transformations on pure ASTs. At the point of the pure
AST, there is zero typing information available, and lombok is not
aware of the filer (the thing that uses the classpath and, in javac,
sourcepath, and in eclipse, your project dependencies, to figure out
what "List" is supposed to refer to). In other words, lombok is not
currently capable of figuring out that "List" is in fact referring to
"java.util.List", and even if it knew that this is about
java.util.List, it would have no way of figuring out which methods are
in java.util.List (well, List is in the runtime, but what if you want
to @Delegate one of your own types and not something in the core java
libraries? That should obviously work too before we release such a
feature).

It won't be too hard to add it, and with typing info you can do a lot
more than @Delegate.


Note how you don't need to mark your class abstract, and you also do
not need to use a special format to construct instances of this class.
There will also be zero runtime dependencies - it's all sorted out at
compile time, exactly as if you wrote all those wrapper methods
yourself.


If someone has a better idea I'd love to hear it. I can't take credit
for it; someone on the lombok googlegroup at http://groups.google.com/group/project-lombok
suggested it, and I copied the entire idea verbatim into the near
future planning.

On Aug 26, 11:50 pm, Mark Derricutt <m...@talios.com> wrote:
> After Project Lombok was first mentioned here I was thinking how this could
> work with mixins, and again after listening about the mixins for java
> (hereon known as M4J) project mentioned on the last show, and watching the
> video athttp://www.berniecode.com/writing/java-mixins/I was thinking about

Mark Derricutt

unread,
Aug 26, 2009, 9:44:01 PM8/26/09
to java...@googlegroups.com
Without being abstract, and without IDE support this would throw errors as the class/source doesn't implement the List interface, but lombok could (if possible) drop the abstract bytecode marker, and use an implementation mentioned in the annotation.

public abstract class Something extends Whatever implements List {  
  @Delegate(implementation=MyList.class)
  private List list;
}

And maybe from a factory to give some runtime mixin flexability...

public abstract class Something extends Whatever implements List {  
  @Delegate(implementationFactory=MyListFactory.class)
  private List list;
}




--
Pull me down under...

Reinier Zwitserloot

unread,
Aug 26, 2009, 10:30:45 PM8/26/09
to The Java Posse
Nope; lombok injects those methods well before the latter stages of
the error finding process runs - which is where problematic typing
relations, such as missing methods that you ought to implement due to
an interface, are found. It's just like using "getFoo();" in your own
method when getFoo() is being generated by lombok. Just works. Lombok
isn't an annotation processor - it uses annotation processing (or, on
eclipse, a javaagent) to hack javac/eclipse and insert itself in
between the AST tree builder and everything else javac/eclipse does.
Such as error checks, compilation, building auto-complete dialogs, the
overview window, and even search features such as 'find declaration'.

Not much need for a factory, I think; you might as well do:

@Delegate
private List list = MyListFactory.getInstance().createList(this);

or some such.

On Aug 27, 3:44 am, Mark Derricutt <m...@talios.com> wrote:
> Without being abstract, and without IDE support this would throw errors as
> the class/source doesn't implement the List interface, but lombok could (if
> possible) drop the abstract bytecode marker, and use an implementation
> mentioned in the annotation.
>
> public abstract class Something extends Whatever implements List {
>   @Delegate(implementation=MyList.class)
>   private List list;
>
> }
>
> And maybe from a factory to give some runtime mixin flexability...
>
> public abstract class Something extends Whatever implements List {
>   @Delegate(implementationFactory=MyListFactory.class)
>   private List list;
>
> }
>
> --
> Pull me down under...
>

Mark Derricutt

unread,
Aug 26, 2009, 11:31:08 PM8/26/09
to java...@googlegroups.com
For javac maybe, but not for IDEA or Netbeans, or eclipse without the lombok plugin (thats more what I was meaning).

Reinier Zwitserloot

unread,
Aug 27, 2009, 8:34:35 AM8/27/09
to The Java Posse
You're going to get a bunch of errors if you attempt to edit lombok
code in a smart editor that doesn't have lombok support installed,
yes. It's unavoidable.

On Aug 27, 5:31 am, Mark Derricutt <m...@talios.com> wrote:
> For javac maybe, but not for IDEA or Netbeans, or eclipse without the lombok
> plugin (thats more what I was meaning).
>

Graham Allan

unread,
Aug 27, 2009, 3:50:15 PM8/27/09
to java...@googlegroups.com
> If someone has a better idea I'd love to hear it.

I don't proclaim that, but I have a suggestion.

This seems to be such a similar idea to traits* that it may be worth making
the leap to them if you consider them to be more powerful. One thing with the
delegates idea is that they don't allow 'bolting on' the behaviour if you
don't have access to the source file (correct me if I'm wrong).

An example of this could be the mixin demonstrated by the java-mixins creator:

MyObject myObject = new MyObject() {
@With(interface = MutableTreeNode.class,
implementation = MutableTreeNodeTrait.class)
}

Would make MyObject implement MutableTreeNode and have an implementations
methods bolted in. Of course, there's drawbacks to this that aren't there
with @Delegate: the new class created by the above example has the
performance disadvantages of being compiled to a new class. @Delegates can
also have state, which could be beneficial.

May be off-target with some of these comments: I'm sure you'll let me know ;-)

But the @Delegate description sounds really good.**

* maybe not in Scala, I'm not familiar with exactly how they work.
** I have thought how it would be useful before:
http://stackoverflow.com/questions/254276/would-syntax-for-composition-be-a-useful-addition-to-java

Reinier Zwitserloot

unread,
Aug 28, 2009, 10:10:13 AM8/28/09
to The Java Posse
I think you're mixing two different (but similar) concepts together.
The basic traits feature of scala allows you to define default
implementations in interfaces, which any implementing classes
automatically pick up on unless you override them. It's very similar
to multiple inheritance, except that there's no sharing of state: A
trait's fields aren't inherited by any classes that implement the
trait. Just the methods.

Your proposed syntax isn't java-legal. You can't just dump an
annotation inside a class scope, it has to be attached to something.
Lombok can sort of hack grammar (see the disableCheckedExceptions
hack), but if we're going to hack grammar, you have the freedom to
come up with a better syntax than that.

Syntax-wise, trying to be vanilla-java-grammar compatible is going to
look ugly and hacky, so lets just bite the bullet on that one and
accept that it'll need a grammar hack. Then you could make this as
simple as:

MyObject myObject = new MyObject() with MutableTreeNode: new
MutableTreeNodeTrait();

Note how we're passing in an object and not a class (unless you
intended this to actually copy over the implementation, which is going
to make upgrading things a massive pain in the behind, passing in an
instance is easier all around. Less reflection = win). You can also
chain this, with commas:

MyObject myObject = new MyObject() with java.util.List: new ArrayList
(), Runnable: new Runnable() {
public void run() { /* run code */ }
};


A third aspect of traits is that you can have an interface (well, a
trait, in scala speak) implement some but not all methods; whatever
isn't implemented MUST be implemented by subclasses (i.e.: abstract).
You can't do this with the @Delegate proposal as easily. But then
again, in scala you can't use a trait unless it's specifically marked
as a trait, and @Delegate suffers from no such drawbacks. Fixing this
to be similar to scala is going to be a lot more hacky: lombok would
have to generate calls into an abstract class, so in practice it would
have to create a subclass of the abstract class, add dummy
implementations for all abstract methods (they'll never be called),
and generate wrapper calls into this dummy subclass for all methods
that did have implementations. Fortunately, if we're going to mess
with the grammar, we're in the clear, mostly:

public interface Similarity {
public boolean isSimilar(Object other);
public boolean isNotSimilar(Object other) {
//Hey, lookit, a method body in an interface. Lombok can make
it legal.
return !isSimilar(other);
}
}

which lombok would translate to an interface together with an abstract
class that implements the interface, as an inner class of the
interface (that's java legal today). You can then write something
like:

public class Foo with Similarity {
public boolean isSimilar(Object other) { return false; }
}

The 'with' there indicates you want the mixin functionality (we'll
leave implements alone, don't want to add too many surprises, and
we've already decided 'with' is going to be a keyword). Lombok will
then have to do rather a lot of magic to make this tick; it'll
subclass the abstract class inside Similarity as a non-static inner
class of Foo, adding wrapper calls back to the Foo instance for all
abstract methods (e.g. isSimilar). It'll then add wrapper calls into
this inner class for all methods in the Similarity interface that have
implementations (e.g. isNotSimilar), and which have not been
implemented directly by Foo. A pretty big wart in all this is that
changing Similarity later without also recompiling Foo is not going to
update Foo (whereas if you did this with subclasses, it would update),
but I don't think there's a way around that, the JVM just isn't that
flexible. I doubt scala do that either, in fact.

Bottom line: Scala's "with" keyword is pretty neat. Lombok certainly
doesn't aim to turn java into scala, but taking only the truly
excellent bits of scala should be good.

Hacking grammar rules is much more complicated than @Delegate though -
@Delegate is a couple orders of magnitude easier to add to Lombok, so
we'll start with that, and see where it goes.
> ** I have thought how it would be useful before:http://stackoverflow.com/questions/254276/would-syntax-for-compositio...

James Iry

unread,
Aug 29, 2009, 12:32:26 PM8/29/09
to java...@googlegroups.com
For various practical reasons it's usually better to not define fields in Scala traits, but Scala does allow it

scala> trait Foo { var x = 42 }
defined trait Foo

scala> class Bar extends Foo
defined class Bar

scala> val b = new Bar
b: Bar = Bar@8be1c9

scala> b.x
res2: Int = 42

scala> b.x = 13

scala> b.x
res3: Int = 13

The main difference between Scala traits and true blue multiple class inheritance in C++ and Python is that Scala traits can only have a default no-arg constructor.  If you need constructor arguments then you must use a class.
Reply all
Reply to author
Forward
0 new messages