With this approach "To enrich an interface using traits, simply define a trait with a small number of abstract methods—the thin part of the trait’s interface—and a potentially large number of concrete methods, all implemented in terms of the abstract methods. Then you can mix the enrichment trait into a class, implement the thin portion of the interface, and end up with a class that has all of the rich interface available."
The following Scala code is an example:
class Point(val x: Int, val y: Int)
trait Rectangular {
// abstract methods
def topLeft: Point
def bottomRight: Point
def left = topLeft.x
def right = bottomRight.x
def width = right - left
}
class Rectangle(val topLeft: Point, val bottomRight: Point) extends Rectangular {
// other methods...
}
With this setup we can write:
val rect : Rectangular = new Rectangle(new Point(1, 1), new Point(10, 10))
println(rect left) //prints 1
println(rect right) //prints 10
println(rect width) //prints 9
We will now have a peek and the resulting decompiled .class files and see how this magic looks like from the Java point of view (1). The Point class is unsurprising:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int x() { return x; }
public int y() { return y; }
}
Things become more interesting with Rectangular. The trait results in two Java classes: Rectangular.java and Rectangular$class.java.
public interface Rectangular {
public abstract Point topLeft();
public abstract Point bottomRight();
public abstract int left();
public abstract int right();
public abstract int width();
}
public abstract class Rectangular$class {
public static int left(Rectangular $this) {
return $this.topLeft().x();
}
public static int right(Rectangular $this) {
return $this.bottomRight().x();
}
public static int width(Rectangular $this) {
return $this.right() - $this.left();
}
public static void $init$(Rectangular rectangular) {}
}
Effectively the trait is broken up into two parts - an interface part (Rectangular.java) and an implementing class (Rectangular$class.java) which is not related to Rectangular at all and carries the implemented trait methods as static Java methods.
Finally, the Rectangle class looks like this:
public class Rectangle implements Rectangular {
private final Point topLeft;
private final Point bottomRight;
public Rectangle(Point topLeft, Point bottomRight) {
this.topLeft = topLeft;
this.bottomRight = bottomRight;
Rectangular.class.$init$(this);
}
public Point topLeft() { return topLeft; }
public Point bottomRight() { return bottomRight; }
// the mixin
public int left() { return Rectangular.class.left(this); }
public int right() { return Rectangular.class.right(this); }
public int width() { return Rectangular.class.width(this); }
}
where we see that mixin logic is statically delegated to Rectangular.(1) Compiled with Scala 2.11, decompiled with Jad.
No comments:
Post a Comment