Multimethods for Java

June 23rd, 2006 david

Dynamic dispatch is a mechanism for selecting a method based on the runtime types of the parameters supplied. Java dispatches instance methods dynamically, using the runtime type of the receiver to choose the code to invoke and ignoring the types of the other parameters (just like Python and many other object-oriented languages). This is called single dispatch. Unfortunately, Java’s dispatching is limited in two important ways: it doesn’t allow class extensions, and it doesn’t support multiple dispatch, as implemented in many other object-oriented languages.

I have written some code which uses reflection and proxy generation to conveniently implement dynamic multiple dispatch for Java. It uses C3 linearization to determine the method to invoke. This algorithm was originally devised for Dylan. You can get the distribution here.

This implementation supports subtyping of both arrays and primitive Java types such as int, byte, char, but does not yet support Java 1.5’s generics. The subtyping relation for arrays and primitive types is based on Java’s notion of assignability - see the documentation for details.

Here’s a trivial example of using the dynamic dispatch library:

  import net.lshift.java.dispatch.DynamicDispatch;

  // define an interface
  public interface NumberPredicate {
      public boolean evaluate(Number n);
  }

  // implement it for some argument types
  public class Exact {
      public boolean evaluate(Float f)  { return false; }
      public boolean evaluate(Double f) { return false; }
      public boolean evaluate(Number n) { return true; }
  }

  // create a dynamic dispatcher
  NumberPredicate exact = (NumberPredicate)
    DynamicDispatch.proxy(NumberPredicate.class, new Exact());

Now, code making use of exact can call the evaluate method with a Number instance, and the DynamicDispatch proxy that is backing the NumberPredicate interface will find the most appropriate method on Exact to invoke based on the runtime type of the argument to evaluate. So, for instance:

  exact.evaluate(new Float(12.3)); // returns false.
  exact.evaluate(new Integer(34)); // returns true.

There are several reasons having dynamic dispatch (and multiple dispatch) is useful when programming for Java:

  • If you want to extend an existing set of classes (for instance, to add an aspect - see below), normally you’d create an interface encapsulating your new feature, and make all the classes in the set implement it. If you are not able to modify the classes, one approach is to create wrapper classes for each, and somehow choose a wrapper class at runtime based on the type of the object you’re working with. You might implement this by myObject.getWrapperClass() - but wait! This makes getWrapperClass a new method that needs to be added: precisely the problem you set out originally to solve.

    Dynamic (single) dispatch helps you out here by conveniently automating the required wrapping and method selection.

  • You might also want to use multiple dispatch, dispatching on the types of multiple arguments. Neither the Java language nor the Java virtual machine supports multiple dispatch. In some cases overloading suffices, but in many it does not.

    Dynamic multiple dispatch reinterprets Java’s notion overloading as actual multimethod dispatch. Syntactically, you’re writing overloaded methods - but using DynamicDispatch, the semantics are those of full multiple-dispatched generic functions.

Dynamic dispatch is useful for adding aspects. For example, you might use dynamic dispatch to write a Java object pretty printer, or custom serializer. I’ve employed it for writing a general equality function which is independent of the object’s own implementation of equals, which I find useful in unit tests. I’ll write about that in a later post.

If this kind of thing interests you, MultiJava, a compiler for a multiply-dispatched variant of Java, might be worth a look.

Entry Filed under: Technology, Programming, Our Software

8 Comments Add your own

  • 1. arun  |  July 4th, 2006 at 10:12 am

    i aml confusing in dynamic dispatch, double dynamic dispatch , multiple dynamic dispatch.Can you explain me in a simple way.(the basic of it with examples.)

  • 2. david  |  July 13th, 2006 at 1:58 pm

    http://en.wikipedia.org/wiki/Multiple_dispatch

    This covers it as well as I am going to be able to

  • 3. jan  |  April 17th, 2008 at 1:09 pm

    I’m working on an event dispatching mechanism for a event based integration framework written in Java. I would like to do dynamic dispatching in order to allow for developers to define event listeners that accept only selected event types based on the (overloaded) method signatures they provide. Your code looks very promising, but I’m wondering, whether it can be used with my own class hierarchy as it seems to support only primitive types. Do you think it could easily be extended to handle arbitrary types?

    thanks a lot,
    Jan

  • 4. david  |  April 18th, 2008 at 2:17 pm

    There is special handling for java primitive types, but all types are supported.

  • 5. david  |  April 18th, 2008 at 2:44 pm

    Have a look at DynamicDispatchTest.java, which creates various classes, and then does dynamic dispatch using them.

    Here is a new version of
    lshift-java
    BTW. I don’t think there are any significant changes to the dynamic dispatch code, however.

  • 6. jan  |  April 25th, 2008 at 12:22 pm

    Hi David,

    thanks a lot for your replies and the link to the new version. I’ll definitely try to integrate your dispatching. Just in case you are interested, I’m working within a research project developing an open source EBI framework for the integration of robotic systems. I’ll post a link here as soon as there is anything presentable available.
    Thanks again.

    Jan

  • 7. Jan Schaefer  |  May 4th, 2008 at 9:38 pm

    Hi again,

    when tinkering with DynamicDispatch I found that inherited methods are not invokable on the generated proxy:


    package net.lshift.java;

    import net.lshift.java.dispatch.DynamicDispatch;

    public class DynamicDispatchTest
    {

    /* the event hierarchy:
     * Event
     *  
     *   SystemEvent
     *    
     *     AdditionEvent
     */
    
    // define event listener interface
    public interface EventListener
    {
        public boolean handleEvent(Event e);
    }
    
    // implement it for some argument types
    public class SuperEventListener implements EventListener
    {
        public boolean handleEvent(Event e) {
            System.out.println("handleEvent(Event) in SuperEL called");
            return true;
        }
    }
    
    public class MySystemEventListener
        extends DynamicDispatchTest.SuperEventListener
    {
        public boolean handleEvent(SystemEvent e) {
            System.out.println("handleEvent(SystemEvent) in SE called");
            return true;
        }
    }   
    
    public void basicDispatchTest()
    {
        // create a dynamic dispatcher proxy
        DynamicDispatchTest.EventListener aListener =
            (DynamicDispatchTest.EventListener) DynamicDispatch.proxy(
                DynamicDispatchTest.EventListener.class,
                new MySystemEventListener());
    
        // this works fine
        aListener.handleEvent(new AdditionEvent());
        // this causes a NoSuchMethodError
        aListener.handleEvent(new Event());
    }
    

    }

    This is a feature we definitely need, so I started to dive into your code a bit deeper. What I came up with so far is, that by changing lines 348 and 349 in file net.lshift.java.dispatch.DynamicDispatch.java to call Class.getMethods() instead of Class.getDeclaredMethods() inherited methods become callable and the test will run fine:


    // net.lshift.java.dispatch.DynamicDispatch.java

    public class DynamicDispatch
    {

    [...]
    
    public static class MultiClass
    {
        Map<Signature, Method> shortcuts = new HashMap<Signature, Method>();
        Map<Method, Procedure> procedures = new HashMap<Method, Procedure>();
    
        protected MultiClass(Class constraint,
            Class<? extends Object> implementation)
        {
    

    // Method [] procedures = constraint.getDeclaredMethods();
    // Method [] methods = implementation.getDeclaredMethods();
    Method [] procedures = constraint.getMethods();
    Method [] methods = implementation.getMethods();
    for(int p = 0; p != procedures.length; ++p) {
    this.procedures.put
    (procedures[p], new Procedure(procedures[p], methods));
    }
    }

        [...]
    
    }
    
    [...]
    

    }

    Now I’m wondering whether this might have undesirable side effects. Do you see any problems with the changes I made?

    Thanks once more,
    Jan

  • 8. Jan Schaefer  |  May 4th, 2008 at 9:49 pm

    Oups, for some reason the code blocks were scrambled so I submitted the code samples to pastebin,com.

    The test:
    http://pastebin.com/f49ab08de

    My changes:
    http://pastebin.com/f7d93e5eb

Leave a Comment

Required

Required, hidden

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Trackback this post  |  Subscribe to the comments via RSS Feed

Calendar

June 2006
M T W T F S S
« May   Jul »
 1234
567891011
12131415161718
19202122232425
2627282930  

Most Recent Posts