package com.example.foo; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; /** * This is to demponstrate how to implement {@link Comparable} in a decorator class. *

* Blog post: https://humanoidreadable.wordpress.com/2014/12/15/decorator-implementing-comparable/ *

* {@link IFoo}: The abstract model.
* {@link Foo}: Some basic implementation.
* {@link IFoo}: The decorator.
* {@link Proxy}: Proxy (reflection). * * @author Claude Martin * */ public class SomeClass { public static void main(String[] args) throws Exception { // Classic decoration: IFoo foo0 = new Foo(42); IFoo foo1 = new FooDecorator(foo0); IFoo foo2 = new Foo(1337); IFoo foo3 = new FooDecorator(foo2); // Decorated decorator: [[1337]] IFoo foo4 = new FooDecorator(foo3); // Decoration by Proxy: IFoo.Handler handler = SomeClass::handle; IFoo foo5 = IFoo.decorate(foo0, handler); IFoo foo6 = IFoo.decorate(foo2, handler); IFoo foo7 = IFoo.decorate(foo3, handler); IFoo foo8 = IFoo.decorate(foo7, handler); // foo9->foo7->foo3->foo2: [{[1337]}] IFoo foo9 = new FooDecorator(foo7); List foos = asList(foo0, foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9); for (IFoo a : foos) { for (IFoo b : foos) { int i = a.compareTo(b); System.out.println(format("%s.compareTo(%s) = %d", a, b, i)); boolean e = a.equals(b); System.out.println(format("%s.equals(%s) = %s", a, b, e)); } } } /** * Wraps toString in "{?}". * * @see IFoo#decorate(IFoo, com.example.foo.SomeClass.IFoo.Handler) */ private static Object handle(IFoo foo, Method method, Object[] arguments) throws Throwable { // decoration: if (method.getName().equals("toString")) return "{" + foo.toString() + "}"; // delegation: return method.invoke(foo, arguments); } /** * A decorator of an instance of IFoo. * * This decorates the toString method by wrapping the value in "[?]". */ public static class FooDecorator implements IFoo { private final IFoo field; public FooDecorator(IFoo field) { super(); this.field = requireNonNull(field, "field"); } @Override public int hashCode() { return this.field.hashCode(); } @Override public boolean equals(Object other) { return this == other || this.field.equals(other); } @Override public int compareTo(IFoo other) { return this.field.compareTo(other); } @Override public int getValue() { return this.field.getValue(); } @Override public String toString() { return "[" + this.field.toString() + "]"; } } /** * Implementation of IFoo. * * I recommend to make this class final or even immutable. */ public/* final */static class Foo implements IFoo { private final int value; public Foo(int value) { this.value = value; } @Override public int compareTo(IFoo other) { return Integer.compare(this.getValue(), other.getValue()); } @Override public int getValue() { return this.value; } @Override public String toString() { return Integer.toString(this.value); } @Override public int hashCode() { return this.value; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof IFoo)) return false; IFoo other = (IFoo) obj; if (this.getValue() != other.getValue()) return false; return true; } @SuppressWarnings("static-method") public boolean canEqual(Object other) { return (other instanceof Foo); } } /** * The interface which models the abstract idea of the "Foo". */ interface IFoo extends Comparable { public int getValue(); /** * TODO: This should explain in great detail when two instances of IFoo are equal. In this case * it would be equal if both have the same value. But what if some implementation has more * fields? cf: http://www.artima.com/lejava/articles/equality.html */ @Override public boolean equals(Object obj); /** * TODO: Any type with a special definition of {@link #equals(Object)} should also specify how a * hashCode must be calculated. In this case it's just the value. */ @Override public int hashCode(); /** * TODO: This should explain in great detail how two instances of IFoo are to be compared. In * this case it compares the values. But what if some implementation has more fields? */ @Override public int compareTo(IFoo o); /** Decorates an instance of IFoo using a given handler. */ public static IFoo decorate(final IFoo foo, final Handler handler) { requireNonNull(foo, "foo"); requireNonNull(handler, "handler"); return (IFoo) Proxy.newProxyInstance(IFoo.class.getClassLoader(), new Class[] { IFoo.class }, (proxy, method, args) -> { return handler.invoke(foo, method, args); }); } /** Handler of method calls to a decorated instance if IFoo. */ @FunctionalInterface public static interface Handler { public Object invoke(IFoo foo, Method method, Object[] args) throws Throwable; } } }