Implementing an interface without implementing the interface


Say you have a class looks like this.

/**
 * An encoder encodes like a boss.
 *
 * @author a real boss.
 */
public class BossEncoder {

    /**
     * Encodes given {@code decoded} like a boss.
     *
     * @param decoded input bytes to encode
     *
     * @return encoded output bytes.
     */
    public byte[] encode(final byte[] decoded) {
        return decoded; // this is how a boss does his job.
    }
}

On a fine spring day, you requested to modify the class suitable for following interface.

void apache(org.apache.commons.codec.BinaryEncoder encoder);

No sweat.

public class BossEncoder implements BinaryEncoder {

    @Override
    public Object encode(final Object object) throws EncoderException {
        // bosses don't care about 'null's.
        return encode((byte[]) object);
    }

    @Override
    public byte[] encode(final byte[] decoded) throws EncoderException {
        return decoded;
    }
}

What if you don’t want to implement any 3rd party interfaces? What if you shouldn’t?
Dynamic Proxy Classes comes in rescue.

public class BossEncoderProxy implements InvocationHandler {

    private static final Class<?> BINARY_ENCODER_CLASS;

    static {
        try {
            BINARY_ENCODER_CLASS = Class.forName(
                "org.apache.commons.codec.BinaryEncoder");
        } catch (ClassNotFoundException cnfe) {
            throw new InstantiationError(cnfe.getMessage());
        }
    }

    public static Object newInstance() {

        return Proxy.newProxyInstance(BINARY_ENCODER_CLASS.getClassLoader(),
                                      new Class<?>[]{BINARY_ENCODER_CLASS},
                                      new BossEncoderProxy());
    }

    protected BossEncoderProxy() {
        super();

        this.bossEncoder = new BossEncoder();
    }

    @Override
    public Object invoke(final Object proxy, final Method method,
                         final Object[] args)
        throws Throwable {

        if ("encode".equals(method.getName())) {
            return bossEncoder.encode((byte[]) args[0]);
        }

        throw new UnsupportedOperationException("unsupported: " + method);
    }

    private final BossEncoder bossEncoder;
}

Now you can use like this.

final BinaryEncoder binaryEncoder =
    (BinaryEncoder) BossEncoderProxy.newInstance(); // no ClassCastException

apache(binaryEncoder);
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s