Tuesday, August 08, 2006

Interfaces Should Be Modules

Currently, in order to implement a Java interface in JRuby, you extend from it like so:

require 'java'

include_class "java.awt.event.ActionListener"

class MyActionListener < ActionListener
def actionPerformed(event)
puts event
end
end

While documenting a JRUBY-66 workaround and thinking about a longterm fix, it hit me like a diamond bullet through my forehead: interfaces should be treated like modules.

My justification:

  1. You can include many modules, but only extend one class...just like interfaces. Currently in JRuby you can only implement one interface, which is stupid.
  2. Modules imply a particular set of behaviors not specific to a given class hierarchy...just like interfaces.
  3. Ruby implementations of Java interfaces can't extend any other classes; you can't both extend Array and implement Collection, if that were your goal.
  4. Ruby implementations of Java interfaces have bugs when defining initialize, since they don't really just implement that interface...they extend one of our JavaSupport proxies.
Item #1 will be of particular importance as we start using JRuby more and more to implement Java services. In my opinion, this is an unacceptable limitation on JRuby's Java integration capabilities.

Item #3 limits your ability to re-open core Ruby classes and add new Java interfaces to them, something that might greatly simplify mapping Ruby types to Java-land.

Item #4 is the cause of JRUBY-66, since we need to make sure the proxy's initializer is called.

In our defence, we inherited much of this Java integration behavior from the original project owners; however I think mapping interfaces to modules allows for much more powerful and uniform Java integration support.

I know it would be a fairly significant change to make Java interfaces
act like modules, but it seems much more logical to me. It's also primarily a new feature we could phase in, with the < syntax continuing to work for old style interface implementation.

Thoughts?
# yes, I know encapsulation would be better...this is just an example
...
include_class "javax.swing.JButton"

class MyActionRecorder < Array
include ActionListener

def actionPerformed(event)
self << event
end
end