├── README.md ├── src └── main │ └── reflect │ ├── Message.java │ ├── Printer.java │ ├── TextMessage.java │ ├── ReflectedClass.java │ ├── StreamPrinter.java │ ├── MyApp.java │ ├── Composite.java │ ├── Delegate.java │ └── InstanceDelegate.java └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | composr 2 | ======= 3 | -------------------------------------------------------------------------------- /src/main/reflect/Message.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | public interface Message { 4 | public String message(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/reflect/Printer.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | public interface Printer { 4 | void print(String message); 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | -------------------------------------------------------------------------------- /src/main/reflect/TextMessage.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | public class TextMessage implements Message { 4 | private final String msg; 5 | 6 | public TextMessage(String message) { 7 | this.msg = message; 8 | } 9 | 10 | @Override public String message() { 11 | return msg; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/reflect/ReflectedClass.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | public class ReflectedClass { 4 | private Delegate[] delegates; 5 | public ReflectedClass(Delegate ... delegates) { 6 | this.delegates = delegates; 7 | } 8 | public T create() { 9 | return new Composite(delegates){}.create(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/reflect/StreamPrinter.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | import java.io.PrintStream; 4 | 5 | public class StreamPrinter implements Printer { 6 | private PrintStream to; 7 | public StreamPrinter(PrintStream target) { 8 | to = target; 9 | } 10 | public void print(String message) { 11 | to.print(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/reflect/MyApp.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | public interface MyApp extends Printer, Message { 4 | 5 | static Composite IMPL = new Composite( 6 | new InstanceDelegate(){ 7 | @Override protected Object initialValue() { 8 | return new TextMessage("Hello, World!"); 9 | } 10 | }, 11 | new InstanceDelegate(){ 12 | @Override protected Object initialValue() { 13 | return new StreamPrinter(System.out); 14 | } 15 | } 16 | ){}; 17 | 18 | 19 | 20 | static class Main { 21 | public static void main(String ... args) { 22 | MyApp app = MyApp.IMPL.create(); 23 | app.print(app.message()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/reflect/Composite.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | 6 | public class Composite extends Delegate { 7 | public Composite(final Delegate ... delegates) { 8 | super( 9 | new InvocationHandler() { 10 | @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 11 | for (Delegate delegate : delegates) { 12 | try { 13 | Method handler = delegate.getClassTarget().getMethod(method.getName(), method.getParameterTypes()); 14 | return delegate.getHandler().invoke(proxy, handler, args); 15 | } 16 | catch(NoSuchMethodException ex) { 17 | // too bad 18 | } 19 | } 20 | throw new NoSuchMethodException(method.toString()); 21 | } 22 | } 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/reflect/Delegate.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.ParameterizedType; 5 | import java.lang.reflect.Proxy; 6 | import java.lang.reflect.Type; 7 | 8 | public abstract class Delegate { 9 | private InvocationHandler handler; 10 | 11 | public Delegate(InvocationHandler handler) { 12 | this.handler = handler; 13 | } 14 | 15 | public InvocationHandler getHandler() { 16 | return handler; 17 | } 18 | 19 | /** 20 | * Gets the class of the target of this element. 21 | * 22 | *

This method returns the type of the generic type argument {@code T}. 23 | * This is only possible if the generic type argument is filled in on 24 | * the delegate {@code class}. E.G. This will work 25 | * @return 26 | */ 27 | @SuppressWarnings("unchecked") 28 | public Class getClassTarget() { 29 | Type t = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 30 | if (! (t instanceof Class)) throw new IllegalStateException("Unable to determine generic type argument for " + getClass().getSimpleName() + "."); 31 | return (Class) t; 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | public T create() { 36 | Class t = getClassTarget(); 37 | return (T) Proxy.newProxyInstance(t.getClassLoader(), new Class[]{t}, this.handler); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/reflect/InstanceDelegate.java: -------------------------------------------------------------------------------- 1 | package reflect; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.util.concurrent.atomic.AtomicReference; 6 | 7 | public abstract class InstanceDelegate extends Delegate { 8 | private AtomicReference value = new AtomicReference(); 9 | 10 | public InstanceDelegate() { 11 | super(null); 12 | } 13 | 14 | /** 15 | * Gets the instance this delegate is delegating calls to. 16 | * 17 | * @return The object, which will be created by calling {@code initialValue} 18 | * if it did not exist yet, never {@code null}. 19 | * 20 | * @see #set 21 | * @see #initialValue 22 | */ 23 | public final Object get() { 24 | Object result = value.get(); 25 | if (result == null) { 26 | value.compareAndSet((Object) null, initialValue()); 27 | result = value.get(); 28 | } 29 | return result; 30 | } 31 | 32 | /** 33 | * (Re)sets the instance this delegate is delegating calls to. 34 | * 35 | *

If this method is called with a non-{@code null} value 36 | * prior to calling {@code get}, the {@code initialValue} 37 | * method will not be invoked.

38 | * 39 | *

If on the other hand {@code set} is called with a 40 | * {@code null} value, the next invocation of {@code get} 41 | * will again invoke {@code initialValue}.

42 | * 43 | * @param value The value to set, possibly {@code null}. 44 | * 45 | * @see #get 46 | * @see #initialValue 47 | */ 48 | public final void set(Object value) { 49 | this.value.set(value); 50 | } 51 | 52 | @Override public InvocationHandler getHandler() { 53 | return new InvocationHandler() { 54 | @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 55 | return method.invoke(get(), args); 56 | } 57 | }; 58 | } 59 | 60 | /** 61 | * Creates the initial value for the instance this delegate delegates to. 62 | * 63 | *

This method will be invoked when the 64 | * {@code get} method is invoked and the object this delegate is delegating to 65 | * is (still) {@code null}.

66 | * 67 | *

If {@code set} had been called with a non-{@code null} value prior to 68 | * calling {@code get}, this method might never be invoked. On the other hand 69 | * if {@code set} is called with a {@code null} argument, or multiple threads 70 | * are accessing this delegate simultaneously, this method might end up being 71 | * called multiple times.

72 | * 73 | * @return The new instance to be delegated to, never {@code null}. 74 | */ 75 | protected abstract Object initialValue(); 76 | } 77 | --------------------------------------------------------------------------------