One of the problems with working in Ecstasy is that it becomes really annoying to move back to "lesser" languages ;). One Ecstasy feature I miss when working in Java is mixins. You don't need them often, but when you do, mixins can be very, very useful.
In response this JMixin project was created to bring Ecstasy style mixins to Java. While not quite as capable as full-blown Ecstasy mixins, JMixins do get close enough to be quite useful.
But what are mixins in the first place? Simply put mixins offer what is essentially multiple inheritance while avoiding the classic diamond problem. Like many object-oriented languages, Java only supports a single line of inheritance. Inheritance is very useful in that it allows you to "inherit" implementation logic in a new class without having to actually provide (or repeat) that logic. For
example lets imagine we're going to have classes describing different types of vehicles for (land, sea, and air). We could create classes Bicycle
, Car
, Truck
, Boat
, Sub
, Plane
, Helicopter
, etc. All of these classes have at least a loose relationship, they are all types of vehicles. So we can save ourselves some work and define a base Vehicle
class which specifics the properties which are common across all of these:
class Vehicle
{
public int getSeats()
{
return this.seats;
}
public void setSeats(int seats)
{
this.seats = seats;
}
public int getStorageCapacityFt3()
{
return this.storageCapacityFt3;
}
public void setStorageCapacityFt3(int storageCapacityFt3)
{
this.storageCapacityFt3 = storageCapacityFt3;
}
private int seats;
private int storageCapacityFt3;
}
Now we could have all of the above classes extend Vehicle
and we get to avoid redefining the seats
and storageCapacity
fields and accessors over and over again. We can also use multiple levels of inheritance to do some more sharing. Perhaps we should have LandVehicle
which extends Vehicle
and which is extended by Car
and Truck
and defines something like how many doors, and wheels they have. And similarly we could introduce SeaVehicle
abstracting out the commonalities between for Boat
s and Sub
s, and so on. So we'd get a hierarchy more like:
Vehicle
|_____LandVehicle
| |_______Bicycle
| |_______Car
| |_______Truck
|
|_____SeaVehicle
| |_______Sub
| |_______Boat
|
|_____AirVehicle
|_______Plane
|_______Helicopter
This is all well and good as we get to avoid lots of code duplication. There is however a problem. What do we do about sea planes, or duck boats for that matter? These classes would naturally incorporate multiple categories of vehicle, i.e. SeaPlan
should extend from both SeaVehicle
and AirVehicle
. Since Java doesn't have multiple inheritance we'd be left needing to do some code duplication. Also we'd likely want to introduce interfaces so that a SeaPlane
could be used as either a SeaVehicle
or LandVehicle
.
Yikes, so it looks like the above hierarchy should be defined as interfaces, and then we need a second duplicate hierarchy of classes which implement those interfaces, and then some oddball custom implementations for these weird types of vehicles which fall into multiple categories, and again for those we'd end up duplicating some of the logic and state. Then of course someone will come up with a FlyingBicycleSub
and we need further duplications.
So that brings us back to mixins. Mixins allow you to inherit both logic and state, and are not limited to single inheritance. If Java had mixins we'd simply adjust the above hierarchy as follows:
Vehicle
|_____LandVehicle
| |_______Bicycle
| |_______Car
| |_______Truck
| \_______DuckBoat
|_____SeaVehicle /
| |_______Sub /
| |_______Boat
| \________SeaPlane
|_____AirVehicle /
|_______Plane
|_______Helicopter
This would be nice, we'd be back to a single hierarchy and DuckBoat
s and SeaPlane
s would simply inherit (actually incorporate) multiple mixins in order to pick up their logic and state without needing to repeat all sorts of code.
Now lets look at how we can actually achieve the above with Java.
Our Java mixins are defined via interfaces with default
methods to provide their logic, and these mixin interfaces extend a special Mixin
interface. A class which wishes to "incorporate" a mixin needs to just include it in its implements
clause.
For example lets define a DuckBoat
as a class mixing in Truck
and Boat
mixins.
class DuckBoat
extends Mixin.Base implements Truck, Boat
{
// nothing more needed here
}
That's it, we now have a functional DuckBoat
which exposes all the logic as well as state from Truck
, Boat
, LandVehicle
, SeaVehicle
, and Vehicle
, and we can write code like:
DuckBoat quackers = new DuckBoat();
quackers.setSeats(24);
quackers.setWheels(6);
quackers.setDoors(2);
quackers.setProps(2);
quackers.setDisplacement(1234);
And of course quackers
can be passed to methods which expect either a land or sea vehicle.
So how did we pick up the state? As you probably noticed we actually did more than just declare that we implement
some mixins, we also declared that we extend
a special Mixin.Base
class. Mixin.Base
is what is managing the state for the various mixins. If your class already inherits from some other class you'll need to do a bit more work to enable mixins.
class DuckBoat
extends SomeNonMixinCapableClass implements Truck, Boat
{
private final Mixin.State mixin = Mixin.State.of(this);
public final Mixin.State mixin() {return mixin;}
}
That's all that is required, and now DuckBoat
can incorporate as many mixins as it likes without needing to write any additional boilerplate code. Any class extending DuckBoat
can add further incorporations without writing any boilerplate of their own. What we see above is the literal implementation of Mixin.Base
. By the way the mixin()
method is declared on the Mixin
interface, so here we're just providing the implementaiton, and if we forget to the compiler will complain. Also note that we made our implementation final
so that any class which extends DuckBoat
won't accidentally repeat our work and define needless additional copies of the state.
So far we've only looked at mixins without constructors, or more correctly mixins which include a zero-param constructor. Mixins are allowed to have parameterized constructor(s). We've yet to look at how mixins are written, but for the moment know that each mixin has an inner State
class which contains its state, and this is where constructors live. When a class wishes to incorporate a mixin with a parameterized constructor it will need to do the work to instantiate that mixin's State
object.
Let's assume that our Vehicle
mixin will make its fields final
and not have all those setter methods. It will then need a constructor which takes in those field values. DuckBoat
would then need to be altered as follows:
class DuckBoat
extends Mixin.Base implements Truck, Boat
{
public DuckBoat()
{
mixin(new Vehicle.State(/*seats*/ 24, /*storageCapacityFt3*/ 1234));
}
}
The incorporating class needs to ensure that it calls mixin(State)
for any incorporated Mixin
s lacking a public
zero-param constructor. Unfortunately this is not enforced at compile time, and thus failing to provide that State
will result in an exception at runtime when the mixin is accessed. If the State
has both a zero-param constructor and parameterized constructors, then directly instantiating and mixing in the State
is optional as the zero-param variant will be used if no explicit State
is provided.
It is highly recommended that mixing in the State
occur during the construction of the incorporating object, and before the object becomes visible to multiple threads.
It should be noted that it is also a runtime error to mix the same State
type multiple times into an incorporating object. In complex hierarchies it should be assumed that the first class in a hierarchy which incorporates a Mixin
would be the one to call mixin(State)
for that Mixin
. Any deviation from this pattern should be explicitly called out in the class documentation.
That is all that you need to know in order to make use of mixins, but the question remains, how do you write a mixin?
As it turns out authoring a new mixin is similarly easy, and not much harder than implementing a traditional class.
Let's start by writing our Vehicle
mixin:
interface Vehicle extends Mixin
{
default int getSeats()
{
return mixin(State.class).seats;
}
default int getStorageCapacityFt3()
{
return mixin(State.class).storageCapacityFt3;
}
final class State extends Mixin.State
{
private final int seats;
private final int storageCapacityFt3;
public State(int seats, int storageCapacityFt3)
{
this.seats = seats;
this.storageCapacityFt3 = storageCapacityFt3;
}
}
}
We've defined Vehicle
as an interface extending the Mixin
interface and provided defaults for the method it declares. In order to store and access state for a Vehicle
we use a mixin(State.class)
prefix. This can be thought of as similar to this
on a class
's method declaration. What mixin(State.class)
does is find the Vehicle.State
instance within a particular Vehicle
instance. Vechicle.State
is just a normal class which has our fields. You may notice that as compared to our original class
based Vehicle
the line count is nearly identical, we've just added the line final class State extends Mixin.State
and its {}
. Other than that the transformation consisted of replacing public
with default
and this
with mixin(State.class).
Note that with this pattern we mark our inner State
class as final
. This final
ensures that we don't recreate the "diamond problem" when one mixin extends another (more below).
When authoring more complex mixins you may choose to simplify the method implementations a bit by introducing a private
interface helper method to your mixin:
interface Vehicle extends Mixin
{
default int getSeats()
{
return state().seats;
}
default int getStorageCapacityFt3()
{
return state().storageCapacityFt3;
}
private State state() // <----- helper
{
return mixin(State.class);
}
final class State extends Mixin.State
{
private int seats;
private int storageCapacityFt3;
public State(int seats, int storageCapacityFt3)
{
this.seats = seats;
this.storageCapacityFt3 = storageCapacityFt3;
}
}
}
This private
method is also quite useful if your mixin will have type parameters. For example:
interface ValueHolder<V> extends Mixin
{
default V getValue()
{
return state().value;
}
default void setValue(V value)
{
state().value = value;
}
@SupressWarnings("unchecked")
private State<V> state()
{
return mixin(State.class);
}
final class State<V> extends Mixin.State
{
private V value;
}
}
Now let's extend our Vehicle
mixin to make LandVehicle
. As mentioned previously we want to avoid the "diamond problem", but that doesn't mean we can't use interface inheritance, we just need to not use State
inheritance:
interface LandVehicle extends Vehicle
{
default int getWheels()
{
return state().wheels;
}
default void setWheels(int wheels)
{
state().wheels = wheels;
}
private State state()
{
return mixin(State.class);
}
final class State extends Mixin.State // <-- doesn't extend Vehicle.State
{
private int wheels;
}
}
Note we've made LandVehicle
extend Vehicle
and thus inherited all of its behavior, but LandVehicle.State
has no relationship to Vehicle.State
. When a DuckBoat
is instantiated it will have exactly one copy of each inner State
object, and when LandVechile
asks for its State
it will receive the LandVehicle.State
while Vehicle
's methods will receive Vehicle.State
. LandVehicle
can call the public
methods it inherited from Vehicle
to interact with its state as needed. If we wanted deeper integration we could put these two mixins in the same package, and make the fields package private, thus allowing LandVehicle
's methods to directly access Vehicle
's state via mixin(Vehicle.State.class)
.
As seen above the inner State
classes can have parameter based constructors. Ideally they would also have a public
zero-param constructor so that the incorporating classes are not required to do manual State
instantiation. In cases where they cannot it is important that the mixin clearly document this fact as the compiler is not able to enforce that the incorporating class has done the State
instantiation.
That's really all there is to it. From the user's perspective using a DuckBoat
is no different than using any other object, there is no reason for them to even be aware that it is mixin based. For the DuckBoat
author incorporating mixins was trivial requiring at most a few lines of boilerplate, and for the mixin author, writing a mixin required a few simple transformations as compared to having written it as a class
.
As to how this all works under the covers it all comes back to the Mixin.State.of(Mixin target)
method. This method constructs a Mixin.State
object which is backed by a combination of all the derived Mixin.State
s that are incorporated for type of the specified target
. The corresponding mixin(State.class)
method knows how to extract the requested state accordingly. This is not simply some Map<Class<State>, State>
being tacked onto each incorporating object. It is far more efficient in terms of both CPU and memory overhead. The overhead is essentially, one State
object per incorporated mixin plus an additional indirection to obtain that state. In terms of hard numbers, that is around 16 bytes of additional overhead per incorporation, and well under a nanosecond of added indirection time per incorporated mixin.
For an in depth look at the internals see Lets Mix it Up.