Scripting on the Java platform

Using Groovy, Jython, and JRuby for Java development

1 2 3 4 5 Page 4
Page 4 of 5

Calling overloaded Java methods

As previously mentioned, dynamically typed languages don't require type declarations. The downside of this convenience is the hidden trap of calling overloaded Java methods. When calling an overloaded Java method, the scripting runtime has to choose the proper one. Under some circumstances the selected method implementation isn't the expected one, as shown in the next two examples.

Listing 8 shows an overloaded Java class.

Listing 8. An overloaded Java class

package test;

public class NonBlockingConnection  {
        public void write(int i)  {
                System.out.println("writing int " + i);
        }
        public void write(long l)  {
                System.out.println("writing long " + l);
        }
}

Listing 9 shows how Jython would call these overloaded methods.

Listing 9. Jython used for calling overloaded methods

from java.lang import Integer
from test import NonBlockingConnection

# call 1 
NonBlockingConnection().write(214748364700) # call method based on python built-in data type
# prints out:
# writing long 214748364700

# call 2
NonBlockingConnection().write(55)  # call method based on python built-in data type
# prints out:
# writing long 55
# ups, not int?

# call 3
NonBlockingConnection().write(Integer(55))  # pass over a Java object instead of python data type
# prints out:
# writing int 55

Call 2 of the Jython script doesn't perform the expected int-typed Java method. A practical solution to determine the overloaded method is to instantiate the desired Java data type object within the script and to pass it over instead of the scripting-internal type. When you use Groovy, method overloading is a non-issue because Groovy supports both static and dynamic typing.

Calling scripting classes from Java

You can execute scripts within the Java environment by using a script engine. Typically, such script engines also allow you to perform dedicated scripting methods or functions. JSR 223 defines a standard interface Invokable to perform such methods and functions. As you can see in Listing 10, a dedicated function write of the Ruby script will be called.

Listing 10. Invoke a specific function of a Ruby script

String rubyScript = "def write(msg) \r\n" +
                        "      puts msg \r\n" +
                        "end \r\n" + 
                        "\r\n" +
                        " def anotherFunction() \r\n" +
                        "     # do something \r\n" +
                        "end \r\n";


    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("jruby");

    engine.eval(rubyScript);
    
    Invocable inv = (Invocable) engine;
    inv.invokeFunction("write", new Object[] { "hello" });
    // prints out:
    // hello

Some scripting environments such as Jython also support a compiler to produce Java classes. In this case a Jython class can be called like a normal Java class. Only the introspection capability of the generated Java class is limited.

Overriding overloaded Java methods

JRuby and Jython both have support for implementing Java interfaces as well as extending Java classes. But overwriting overloaded Java method brings in new issues for most popular scripting languages. How to define which overloaded Java method should be overridden? Languages such as Python 2.x or Ruby don't support method overloading based on type signatures. This requires handling all overloaded cases of the overridden method. The method implementation has to dispatch all overload cases by introspecting the argument types. Listing 11 shows how to do this using Jython.

Listing 11. Using Jython to call overloaded methods

from test import NonBlockingConnection
from java.lang import Integer
from java.lang import Long

class ExtendedNonBlockingConnection(NonBlockingConnection):
    def write(self, *args):
       # the signature to handle?
       if len(args) == 1 and isinstance(args[0], Integer):
           print 'writing overriden int ' + args[0].toString()
       # no, let the super method do the stuff
       else:
          NonBlockingConnection.write(self, *args)


ExtendedNonBlockingConnection().write(Integer(777))
# prints out:
# writing overridden int 777


ExtendedNonBlockingConnection().write(Long(777))
# prints out:
# writing long 777

Because Groovy also supports static type declaration the overloaded method can be declared by the argument types in the override method signature, as shown in Listing 12.

Listing 12. Using Groovy to call overloaded methods

import test.NonBlockingConnection

class ExtendedNonBlockingConnection extends NonBlockingConnection {
    // overwrite the int method
    def write(int i) {
        println 'writing overridden int ' + i
     }
}

new ExtendedNonBlockingConnection().write((int) 777)
// prints out:
// writing overridden int 777

new ExtendedNonBlockingConnection().write((long) 777)
// prints out:
// writing long 777
1 2 3 4 5 Page 4
Page 4 of 5