“Monkey Patch” Java Objects from JRuby

24 02 2007

Sometimes, when you’re programming Java objects in JRuby, you just wish the object acted more… Rubily. JRuby does a pretty good job creating a more Ruby-like interface for Java objects (by aliasing method names and whatnot), but sometimes you want more. Sometimes you want to Monkey Patch those Java objects!

To follow along, install or build JRuby and then fire-up jirb. And, before we go on, please note:

1) I’m using JRuby trunk (3072 I believe). These examples should work in 0.9.2 and 0.9.1, but I haven’t tested them in those environments. Just be aware.

2) “Monkey Patched” Java methods are only available in Ruby code.

3) Aspects of this code are likely to change in the next release.

With that in mind, let’s look at a completely contrived example. Let’s patch java.lang.String. There’s no good reason to do this sort of patching because JRuby marshals strings between Java and Ruby pretty well. But I picked String because everyone should be down with what a String is and how it works, so I can focus on the technique without having to explain the API.

First things first:

require 'java'

This is one of those aspects of the code that will change. In the next release, Java features will be handled in a module, so they will be mixed in, instead of required. But just use require ‘java’ for now. Trust me 🙂

JString = java.lang.String

Here we assign the String class to a constant (JString). We will refer to Java Strings through this constant from now on. Because Ruby objects are just Class objects assigned to constants, this allows us to treat the Java object just like a Ruby object.

So, what do we want to fix about Java Strings? How about this: Ruby strings have size and length methods. Java Strings only have length. If we want JString to behave more like a Ruby String, we’ll have to fix that.

class JString
  def size
    length
  end
end

That’s it. Now take it for a test drive:

s = JString.new('monkey')
p "#{s} | #{s.size}"

You should see something like this:

monkey | 6

Now, just to prove that we did change the behavior of java.lang.String…

s = java.lang.String.new('patch')
p "#{s} | #{s.size}"

Gives you:

patch | 5

That’s how you re-open a Java class in JRuby.

For this particular change, where we just created an aliased name for an existing method, we did things the hard way. Ruby objects accept a message named alias_method that will, as the name suggests, create a second alias for an existing method.

We could re-open the class and alias the method:

class JString
  alias_method :size, :length
end

or, we could just send the alias_method message to the class object.

JString.send :alias_method, :size, :length

This looks like a good place to stop for now, but never fear. I have enough sample code left over for another post. The next one will demonstrate a more practical use of these techniques.


Actions

Information

5 responses

28 02 2007
Thomas E Enebo

The ability to do this will be a great opportunity for testing code that is not backed by interfaces. I am very excited by this potential. Thanks for providing the example.

17 02 2012
Rudolf Olah

This looks like a much better idea than wrapping a Java class with a Ruby class! I bet the performance is a bit better too?

7 12 2012
Huntington Beach Federal Credit Union

It’s difficult to find well-informed people in this particular topic, however, you seem like you know what you’re
talking about! Thanks

7 03 2013
Orange County Banking

I quite like looking through an article that can make men
and women think. Also, many thanks for allowing for me to comment!

26 09 2014
アメリカンイーグル facebook

not that much of a internet reader to be honest but your blogs really nice, keep it up! I’ll go ahead and bookmark your site to come back later. Cheers
アメリカンイーグル facebook http://atersa.com/img/file/American-Eagle/7-+201409251551200+-.t-3.asp

Leave a reply to Rudolf Olah Cancel reply