All pastes #770007 Raw Edit

Double Dispatch

public java v1 · immutable
·published 2007-11-11 20:50 UTC
rendered paste body
/** * An interface for classes that need to dispatch dynamically on some method by * using the ConcreteDispatcher *  * Implementing classes would overload the dispatch(Object o) method for all the * special cases, while having their own entry method - e.g. 'visit(Object o)' * or 'execute(SomeInterface s)' that would call the * ConcreteDispatcher.dispatch() method. This method calls back on the correct * dispatch() implementation. *  * @author hrehfeld *  */public interface Dispatcher {	/**	 * The name of the dispatching method	 */	public static final String METHOD = "dispatch";	/**	 * All classes need the fallback operating on Object o.	 * 	 * @param o	 *            The Object to dispatch on	 */	void dispatch(Object o);}import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * A Class that provides a static way to double dispatch *   *  Large portions of this code were taken from http://www.javaworld.com/javaworld/javatips/jw-javatip98.html *  and adjusted to be used in a more general and easy way *   * @author hrehfeld * */public class ConcreteDispatcher {	/**	 * A Static way to do double dispatching	 * 	 * Dispatching looks for overloaded Dispatcher.METHOD() methods in the following order:	 * 1) the object's class	 * 2) the object's super-classes (bottom-up)	 * 3) the interfaces the object implements. Order is undefined, thus this might throw an {@link InvocationTargetException}, when the Dispatcher implements methods for more than one of the implemented interfaces, but no methods that would be found in step 1) or 2).	 * 	 * @param dispatcher The dispatcher to call methods on	 * @param object Some object to dispatch on	 * @throws InvocationTargetException When the object implements two interfaces the Dispatcher provides overloaded methods for (and the Dispatcher doesn't provide more specialised methods), behaviour is undefined	 */	public static void dispatch(Dispatcher dispatcher, Object object) throws InvocationTargetException  {			Method method = ConcreteDispatcher.getMethod(dispatcher,Dispatcher.METHOD, object.getClass());			try {				method.invoke(dispatcher, new Object[] {object});			} catch (IllegalArgumentException e) {				// TODO Auto-generated catch block				e.printStackTrace();			} catch (IllegalAccessException e) {				// TODO Auto-generated catch block				e.printStackTrace();			}	}	/**	 * Find the most appropriate method to call on the Dispatcher for some object's class	 *  	 * @param dispatcher The dispatcher to check	 * @param methodName The name of the dispatching method	 * @param class_ Class of the object to dispatch on the Dispatcher	 * @return a Method for the className	 * @throws InvocationTargetException	 */	@SuppressWarnings("unchecked")	private static Method getMethod(Dispatcher dispatcher, String methodName, Class class_) throws InvocationTargetException {		Class newc = class_;		Method method = null;		// Try the superclasses		while (method == null && newc != Object.class) {			try {				method = dispatcher.getClass().getMethod(methodName, new Class[] { newc });			}			catch (NoSuchMethodException e) {				newc = newc.getSuperclass();			}		}		// Try the interfaces.  If necessary, you		// can sort them first to define 'visitable' interface wins		// in case an object implements more than one.		if (newc == Object.class) {			int matches = 0;			for (Class i: class_.getInterfaces()) {				try {					method = dispatcher.getClass().getMethod(methodName, new Class[] { i });					matches++;				}				catch (NoSuchMethodException e) {									}			}			// more than one match 			if (matches > 1) {				throw new InvocationTargetException(new Exception("Error: while trying to dispatch on the interfaces, there's "						+ matches + " possible matches. Behaviour is undefined."));			}		}				if (method == null) {			try {				method = dispatcher.getClass().getMethod(methodName, new Class[] {Object.class});			} catch (Exception e) {				throw new InvocationTargetException(new Exception("Dispatcher doesn't implement " 						+ methodName + "(Object o)!"));			}		}		return method;	}}public interface Foo {}public class AFoo implements Foo {}public class BFoo implements Foo {}import java.lang.reflect.InvocationTargetException;public interface Executor {	void execute(Foo f) throws InvocationTargetException; }import java.lang.reflect.InvocationTargetException;public class DispatchExecutor implements Executor, Dispatcher {	public void dispatch(AFoo o) {		System.out.println("Dispatcher AFoo");	}	public void dispatch(BFoo o) {		System.out.println("Dispatcher BFoo");	}	public void dispatch(Object o) {		System.out.println("Dispatcher Object");	}	@Override	public void execute(Foo f) throws InvocationTargetException {		ConcreteDispatcher.dispatch(this, f);	}	public static void main(String[] args) throws InvocationTargetException {		Foo a = new AFoo();		Foo b = new BFoo();		DispatchExecutor d = new DispatchExecutor();				d.execute(a);		d.execute(b);	}}outputs: Dispatcher AFooDispatcher BFoo