Java reflection on a message using MethodHandle

Some code I was looking at recently had an overloaded method and each method just accepted a different type argument. With Generics there are probably ways to reduce this clutter, or not.

Anyway, I started thinking about the original concept of OO; it was all about ‘messages’. But what is the difference between a ‘message’ as per Alan Kay and Smalltalk and a method’, née “subroutine” in Java? I guess it’s intent, design, and the other features of OOP?

Jump to example source code

Following this line of thought I wound up looking at how to invoke a public method on a class and that method in turn delegates to private methods that match the data within the message. BTW, this is not a new concept, it is an often used ‘idiom’, like in servlets and other backend service handlers.

The public method is the destination of a ‘message’. So I searched the web and found mention of the usual Reflection, Proxies, instanceof, and finally MethodHandles. The package java.lang.invoke contains the new approach to reflection introduced in Java 7. MethodHandle is the new class that makes all this possible.

Not much on MethodHandle on the web (search instead for: java “method handle”), but the API docs are very good and this blog post, “A glimpse at MethodHandle and its usage”, is all you need to get started. Below in Listing 1, a simple example is shown. Class Server has a single method that takes a String message and a parser.

The message contains the data that is used to determine the actual method to invoke. Simple, but the point is that this could be a JSON/XML string or some other data container. The parser is just me experimenting with callbacks, here the callback gets the data in the message. Yeah, I know, if the client can get the data, why not just send it? It’s just a ‘hello world’ example.

Example
In listing 1 the Service class exposes one method, send.

public class Service {	
    public Object send(String message, Parser parser) throws Throwable {
	Object pData = parser.parse(message);		

	MethodType methodType = 
          MethodType.methodType(
            Object.class,pData.getClass());		

	MethodHandle mh = MethodHandles.lookup()
         .findVirtual(
            Service.class, "call", methodType);		

	Object actual = mh.invoke(this,pData);		
	return actual;
    }

    @SuppressWarnings("unused")
    private Object call(String data) {
	System.out.println("in string method ...");
	return "string data";
    }

    @SuppressWarnings("unused")
    private Object call(Integer data) {
	System.out.println("in int method ...");
	return new Integer(42);
    }
	
    public interface Parser{
	public Object parse(String s); 
    }
}

Listing 1

In listing 2, the Main class sends the message to the Service class.

public class Main {    
    public static void main(String[] args) throws Throwable {
      Service app = new Service();
      Service.Parser parser = new Parser();   

      Object actual = app.send("some text", parser);
      assert actual == "string data" 
        : "Should have returned 'some data' string";
        
      actual = app.send("number: 42",parser);
      Integer expected = 42;
      assert (actual.equals(expected)) 
         : "Should have returned Integer with value 42";
  }
    
  static class Parser implements Service.Parser{                  
      @Override
      public Object parse(String s) {
          if(s.startsWith("number:")){                            
              Integer i = Integer.valueOf(  
                  (s.split(":")[1]).trim() );
              return i;
          }
          
          return s;
      }
  }
}

Listing 2

Not earthshaking of course. The output is:
in string method …
in int method …

The example is somewhat like the javax.servlet.Servlet class, only one method, service(ServletRequest req,ServletResponse res) is also used, and usually one invokes other methods based on the “message” params.

I can see the use of MethodHandles leading to some powerful mojo.

Links

Similar Posts:

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Leave a Reply

Your email address will not be published. Required fields are marked *