User Tools

Site Tools


Sidebar

Dave Orme muses about data-first development.

My current work emphasizes data engineering and analysis using Kubernetes, Clojure, Scala, Eclipse, and Google Cloud Platform or AWS.


Blog

The Cloud

Scala, Clojure, and FP

Data-First Development

Agile

Older work

Coconut Palm Software home


Donate Bitcoin:

1Ecnr9vtkC8b9FvmQjQaJ9ZsHB127UzVD6

Keywords:

Kubernetes, Docker, Streaming Data, Spark, Scala, Clojure, OSGi, Karaf, GCP, AWS, SQL

Disclaimer:

Everything I say here is my own opinion and not necessarily that of my employer.

blog:java_does_duck_typing

Java Does Duck Typing

As promised, this is the followup to Duck Typing helps automatically dispose SWT Images. Here, we will show how to implement Duck Types in Java. (If you don't know what a Duck Type is to begin with, it would be worthwhile to read the older post and then come back here.)

What was Duck Typing again?

Duck Typing is a powerful feature included primarily in newer languages. It provides the following capability: An object having all the methods described in an interface can be made to implement that interface dynamically at runtime, even if the object's class does not include the interface in its implements clause.

This is actually a somewhat restricted definition of Duck Typing compared to other languages. But it is the one I have chosen because it is the one I believe fits best with Java's design philosophy of having API contracts explicitly specified in interfaces up-front.

The good news is that although this feature is not built into the Java language, it turns out to be quite easy to implement. But first…

Why should I care about Duck Typing?

One of the things that make good frameworks like SWT such a pleasure to work with is that properties are named very consistently throughout.

For example, labels have a Text property, Combo has a Text property, Shell has a Text property, and, naturally, Text has a Text property.

So, when working with SWT, you quickly learn that any time you want to set the display text of something, you look for a Text property to set. And in the case of setting display text, you would be right 100% of the time.

Unfortunately, this does not hold true for programs that want to manipulate SWT controls. There is no ITextField interface that is implemented by everything with a Text property, for example. And, as we mentioned last time, it would be silly for such a thing to exist, because there are so many naming conventions applied so consistently throughout SWT that every SWT class would be cluttered with interface implementations.

Consequently, it would be nice to be able to define an ITextField interface after the fact and apply it to any SWT class that happens to implement all of the methods specified by the ITextField interface.

“If it walks like a duck, looks like a duck, quacks like a duck, it must be…”

In our last installment, we showed how to hack duck typing semantics using Java reflection. Our example was a class that could automatically set an Image on any control having a setImage() method, then register itself as a Dispose listener. The widgetDisposed() method would then automatically dispose the Image when the parent is automatically disposed by its container.

Recall that the interesting part of the code was the following:

    public ImageMgr(Control receiver, Image image) {
        this.image = image;
        try {
            // Use duck typing--any class with setImage() will work
            Method setImage = receiver.getClass().getDeclaredMethod("setImage",
                                   new Class[] {Image.class});
            setImage.invoke(receiver, new Object[] {image});
        } catch (Exception e) {
            // Programmer error: throw a RuntimeException
            Logger.log().error(e, "Unable to setImage()");
            throw new RuntimeException(e);
        }
        receiver.addDisposeListener(this);
    }

Although this code is clear enough, there are several problems with it:

  • The API contract is completely implicit here. There is no interface anywhere that tells us what methods we expect “receiver” to implement, with what parameters, etc.
  • Data-types are matched completely at run-time using Java reflection. Thus, we completely give up compile-time type safety.

However, we could neatly solve both of these problems if we could somehow make “receiver” implement an interface:

    interface IImageHolder {
       void setImage(Image image);
       void addDisposeListener(DisposeListener disposeListener);
    }

and then just use a variable of type IImageHolder to access “receiver”.

This is exactly what Duck Typing allows us to do–to implement an interface at runtime. So all that remains is to figure out a way to implement this in Java.

In other words, we want to be able to rewrite our constructor to read something like this:

    public ImageMgr(Object receiver, Image image) {
        if (!DuckType.instanceOf(IImageHolder.class, receiver)) {
            throw new ClassCastException("Cannot implement IImageHolder");
        }

        this.image = image;

	IImageHolder imageHolder = (IImageHolder)
            DuckType.implement(IImageHolder.class, receiver);
	imageHolder.setImage(image);
	imageHolder.addDisposeListener(this);
    }

If we could do this, we would reap several advantages:

  • The API contract for the “receiver” parameter is clearly specified up-front (it's specified by the IImageHolder interface).
  • The code will fail-fast if some method is not implemented in receiver.
  • The new code is more compile-time type-safe.
  • The code is less cluttered.

So how do we write a DuckType class that can do all of this for us? The short answer: dynamic proxies.

What is a dynamic proxy?

A dynamic proxy class is a run-time interpreter for method calls on an interface.

More specifically, A dynamic proxy object is an instance of a class that:

  1. implements the InvocationHandler interface and
  2. is constructed using Java's proxy factory: java.lang.reflect.Proxy.

Let's look at each of those in turn.

  1. The InvocationHandler interface is a way to intercept method calls and do your own thing instead. “Your own thing” could include executing code before the actual method is called, executing code after the actual method is called, or maybe not even calling the actual method at all. You get to decide.

Here is what the InvocationHandler interface looks like:

    public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
  1. java.lang.reflect.Proxy is a factory that lets you associate an instance of an InvocationHandler with any arbitrary Java interface, so that when methods are called on the interface, your InvocationHandler class's invoke() method is called instead.

Implementing Duck Types in Java

How do we use this to implement Duck Types?

First, we will create our InvocationHandler.

Our DuckType class will be an InvocationHandler that sits between an arbitrary interface and a Java object instance. When our invoke() method is called, indicating that somebody called a method on the interface, we will look up the corresponding method on the delegate object and call that instead:

    public class DuckType implements InvocationHandler {
        protected DuckType(Object object) {
            this.object = object;
            this.objectClass = object.getClass();
        }
 
        protected Object object;
        protected Class objectClass;
 
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
        {
            Method realMethod = objectClass.getMethod(method.getName(),
                method.getParameterTypes());
            return realMethod.invoke(object, args);
        }
    }

Second, we will use the java.lang.reflect.Proxy factory to make our DuckType class implement any arbitrary interface.

As we saw in our earlier example, we'll do this by providing a static method inside DuckType called implement() to make an arbitrary object implement an interface via our DuckType InvocationHandler:

    public static Object implement(Class interfaceToImplement, Object object) {
        return Proxy.newProxyInstance(interfaceToImplement.getClassLoader(),
            new Class[] {interfaceToImplement}, new DuckType(object));
    }

Now all that remains is to implement our static instanceOf() method inside the DuckType class. This is just straightforward use of the Java reflection API:

    public static boolean instanceOf(Class intrface, Object object) {
        final Method[] methods = intrface.getMethods();
        Class candclass=object.getClass();
        for (int methodidx = 0; methodidx < methods.length; methodidx++) {
            Method method=methods[methodidx];
            try {
                candclass.getMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                return false;
            }
        }
        return true;
    }

Conclusion

In this article, we have examined more evidence that Duck Typing is a useful tool to have in our Java toolchest. We have looked at a possible reimplementation of the ImageMgr class presented previously using Duck Typing. And we have actually implemented a DuckType class providing Java programmers simple access to this powerful programming technique.

Along the way, we have discovered that this implementation provides the following desirable properties:

  • API contracts for duck types are clearly and completely specified up front via Java interfaces.
  • Calls to methods in a duck interface are now compile-time type-safe.
  • Because we can use the DuckType.instanceOf() method to test candidate objects, we no longer have to rely on exceptions for our error handling, resulting in clearer code that fails-fast if an object does not implement a method in a duck interface.

For reference, here is the complete source code to the DuckType class:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
/**
 * DuckType. Implements Duck Typing for Java.  ("If it walks like a duck,
 * quacks like a duck, it...").  Essentially allows programs to treat
 * objects from separate hierarchies as if they were designed with common
 * interfaces as long as they adhere to common naming conventions.
 *
 * This version is the strict DuckType.  All methods present in
 * interfaceToImplement must be present on the target object.
 *
 * @author djo
 */
public class DuckType implements InvocationHandler {
    /**
     * Causes object to implement the interfaceToImplement and returns
     * an instance of the object implementing interfaceToImplement even
     * if interfaceToImplement was not declared in object.getClass()'s
     * implements declaration.
     *
     * This works as long as all methods declared in interfaceToImplement
     * are present on object.
     *
     * @param interfaceToImplement The Java class of the interface to implement
     * @param object The object to force to implement interfaceToImplement
     * @return object, but now implementing interfaceToImplement
     */
    public static Object implement(Class interfaceToImplement, Object object) {
        return Proxy.newProxyInstance(interfaceToImplement.getClassLoader(),
                new Class[] {interfaceToImplement}, new DuckType(object));
    }
 
    /**
     * Indicates if object is a (DuckType) instace of intrface.  That is,
     * is every method in intrface present on object.
     *
     * @param intrface The interface to implement
     * @param object The object to test
     * @return true if every method in intrface is present on object.  false otherwise
     */
    public static boolean instanceOf(Class intrface, Object object) {
        final Method[] methods = intrface.getMethods();
        Class candclass=object.getClass();
        for (int methodidx = 0; methodidx < methods.length; methodidx++) {
            Method method=methods[methodidx];
            try {
                candclass.getMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                return false;
            }
        }
        return true;
    }
 
    protected DuckType(Object object) {
        this.object = object;
        this.objectClass = object.getClass();
    }
 
    protected Object object;
    protected Class objectClass;
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Method realMethod = objectClass.getMethod(method.getName(), method.getParameterTypes());
        return realMethod.invoke(object, args);
    }
}

(If the brief explanation of dynamic proxies included here is not sufficient, I suggest Googling for any of several excellent tutorials available on the web describing dynamic proxies in more detail.)

~~LINKBACK~~ ~~DISCUSSION:closed~~

blog/java_does_duck_typing.txt · Last modified: 2014/10/17 22:08 (external edit)