Over a million developers have joined DZone.

Exposing An Interface At Runtime Using A Proxy Class

DZone's Guide to

Exposing An Interface At Runtime Using A Proxy Class

· ·
Free Resource
Force an object expose an interface at runtime, through a generated proxy class. It must implement all required methods.
Requires BCEL.

UPDATED: does verification at bridge generation time.
expose(Object, Class) will search for the correct public target type to use.
UPDATED: performance hacks.

Example usage:
public class BridgeTest {
  public interface CharList {
    public int length();
    public char charAt(int i);
  void print(CharList l) {
    for(int i=0; i


   /* Make an object expose an interface at runtime */

import org.apache.bcel.generic.*;
import org.apache.bcel.Constants;

import java.lang.reflect.Method;
import java.util.*;

public abstract class Bridge
	protected C __target; // the implementation of the interface
	private final void __init(Object target) { this.__target = (C)target; }

	// cache already-generated bridges
	private static HashMap
      > cache 
		= new HashMap
	// allow injection of classes from byte arrays
	private static InjectingClassLoader loader = new InjectingClassLoader();

	private static final void cacheSet(Class key1, Class key2, Class value) {
         intermediate = cache.get(key1);
		if(intermediate == null)
			cache.put(key1, intermediate = new HashMap
		intermediate.put(key2, value);
	private static final Class cacheGet(Class key1, Class key2) {
           intermediate = cache.get(key1);
		if(intermediate == null)
			return null;
		return intermediate.get(key2);

	// returns [ [ifaceMethods...] [fromMethods...] ]
	private static Method[][] getMapping(Class from, Class iface) throws NoSuchMethodException,IllegalAccessException {
			throw new IllegalArgumentException(iface.getName()+" is not an interface");
			throw new IllegalAccessException(from.getName()+" is not public");
		java.lang.reflect.Method[][] map = new java.lang.reflect.Method[2][];
		map[0] = iface.getMethods();
		map[1] = new java.lang.reflect.Method[map[0].length];

		for(int i=0;i
           > create(Class
             from, Class
             iface) {
		try {
			Method[][] map = getMapping(from, iface);

			String bridgeName = "Bridge_"+from.getSimpleName()+"_"+iface.getSimpleName();
			// public class Bridge_Thing_Mungible extends Bridge
               implements Mungible
			ClassGen cg = new ClassGen(
				Bridge.class.getName(), // superclass name
               ", // source file name
				Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SUPER, // flags
				new String [] { iface.getName() } // interfaces

			ConstantPoolGen cpg = cg.getConstantPool();
			InstructionFactory factory = new InstructionFactory(cg, cpg);

			ReferenceType targetType = (ReferenceType)Type.getType(from);

			for(int i=0; i
		} catch (IllegalAccessException e) { 
			throw new BridgeFailure(from, iface, e); 
		} catch (NoSuchMethodException e) { 
			throw new BridgeFailure(from, iface, e); 
	private static 
                 > get(Class
                   from, Class
                   iface) {
//		Pair
                     key = new Pair
                     (from, iface);
                     > bridgeClass = (Class
                     >)cacheGet(from, iface);
		if(bridgeClass == null) {
			bridgeClass = create(from, iface);
			cacheSet(from, iface, bridgeClass);
		return bridgeClass;

	// Expose interface iface by creating a proxy. 
	// the type of target must be from, a subclass of from, or a class implementing from.
	// from must be public, and must expose all methods in iface.
	public static 
                      I expose(Object target, Class from, Class iface) {
		try {
                       > c = Bridge.get(from, iface);
                        proxy = c.newInstance();
			return (I)proxy;
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
	// Expose interface iface by creating a proxy. 
	// Tries expose(target, target.getClass(), iface) first.
	// then works up the class hierarchy. If doesn't find a public superclass 
	// that exposes all methods, it tries interfaces.
	// if you know in advance the class or interface that exposes the needed methods,
	// use expose(target, Class.forName("ExposingClass"), iface).
	public static  I expose(Object target, Class iface) {
		Class tClass = target.getClass();

		Class from = tClass;
		do {			
			try {
				I result = expose(target, from, iface);
				if(tClass != from) // if tClass == tFrom then get() sets the cache
					cacheSet(tClass, iface, result.getClass());
				return result;
			} catch (BridgeFailure e) {
				Throwable cause = e.getCause();
				if(cause instanceof NoSuchMethodException) {
					// have traced superclass up until the interface isn't satisfied.
					// try interfaces and then give up.
					// in the case of a private implementation of a public interface, 
					// this allows you to subset the interface.
					Class[] targetIfaces = target.getClass().getInterfaces();
					for(Class targetIface : targetIfaces) 
						try {
							I result = expose(target, targetIface, iface);
							cacheSet(tClass, iface, result.getClass()); // perf hack
							return result;
							// ignore, reported exception is that for class hierarchy.
						} catch (BridgeFailure ex) {} 
					throw new BridgeFailure(target, iface, e);
				} else if(!(cause instanceof IllegalAccessException))
					throw e;
			from = from.getSuperclass();
		} while(from != null);
		throw new RuntimeException("Object "+target+" ("+target.getClass().getName()+") has no public superclass");
class BridgeFailure extends RuntimeException {
	public BridgeFailure(Class from, Class iface, Exception cause) {
		super("Could not map class "+from.getName()+" to interface "+iface.getName());
	public BridgeFailure(Object target, Class iface, Exception cause) {
		super("Could not map first public supertype of " + target +
			" (" + target.getClass().getName() + ")," +
			" or any implemented interface, to interface " + iface.getName());
// ClassLoader implementation to let you load classes from byte arrays
class InjectingClassLoader extends ClassLoader { 
	public InjectingClassLoader() {
	public Class load(String name, byte[] buffer) {  
		return defineClass(name, buffer, 0, buffer.length);  

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}