JRuby and Java Arrays

Recently, I’ve been toying with JRuby, and for the most part, I’ve been very pleased with the experience.

In the standard 1.0 JRuby release, most things work surprisingly well. As with any young product, there are always dark spots in the margins, but for the most part the roughest edges are in the Java-to-Ruby integration; something which should be expected to be an evolution for JRuby anyway. Even given that, once you get into the groove of using Ruby syntax and Java objects together, you know what works, what might not work so well, and you can really be quite productive.

One such ‘hurdle’ that I had to overcome was getting from a Ruby array into a Java typed array. JRuby handles coercion most Java types quite well (Ruby strings to Java strings, numbers to numbers, booleans to booleans, and nil to null). Arrays, unfortunately, are not quite as seamless. I also found the documentation on this subject lacking. In any case, let’s look at an example - consider this Java object:

package mypackage;

public class SomeObject { private int[] numbers;

public int[] getNumbers() {
    return numbers;
}

public void setNumbers(int[] numbers) {
    this.numbers = numbers;
}

}

Now, ignoring the fact that, if you’re using JRuby you would probably be better served implementing this in Ruby to begin with, let’s focus on the integration aspect; in other words, this class already exists in Java, and you have to work with it. If you wanted to set the numbers on this object, a logical approach with JRuby would be to do this:

SomeObject = Java::mypackage.SomeObject
object = SomeObject.new
object.numbers = [1,2,3]

If you do, however, buried deep in one of the exceptions you get will be this error message:

TypeError:
expected [[I]; 
got: [org.jruby.RubyArray]; 
error: argument type mismatch

I have moved that on to multiple lines for clarity. In plain English, that’s saying that the method expected an int[], but instead received a RubyArray object. In other words, JRuby didn’t automatically coerce the RubyArray object into an int[] for us. This is something we have to help JRuby with, given our knowledge of the Java object we’re working with. To do this, you have to use the RubyArray method extension to_java, which takes a symbol representing the array type you need:

SomeObject = Java::mypackage.SomeObject
object = SomeObject.new
object.numbers = [1,2,3].to_java(:int)

Now, what if your object expected a Class type as opposed to a primitive; perhaps one of your own objects or a complex Java type? In that case, you simply pass in the constant you declared for that type. Here is a concrete example. Once again, here is the Java object:

public class SomeObject { private StringBuilder[] builders;

public StringBuilder[] getBuilders() {
    return builders;
}

public void setBuilders(StringBuilder[] builders) {
    this.builders = builders;
}

}

As you see, this time it takes an array of java.lang.StringBuilder objects. Here is a block of Ruby code illustrating how you can set these on this object:

SomeObject = Java::mypackage.SomeObject
StringBuilder = java.lang.StringBuilder
object = SomeObject.new
builders = [StringBuilder.new, StringBuilder.new, StringBuilder.new]
object.builders = builders.to_java(StringBuilder)

Comments

What if you need an array of

What if you need an array of interfaces?

[].to_java(java.util.List)

This generates a Java array of java.lang.Object which fails.

Any workaround for this?

Very helpful post. Thanks.

Very helpful post. Thanks.