“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

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

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.




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

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: