Java.next #2: Java Interop

This is Part Two of a series of articles on Java.next. In Part Two, I will look at how Java.next languages interoperate with Java.

Java interop is trivial in all of the Java.next languages. We have Java itself to thank for this–the Java Virtual Machine Specification makes it easy for other languages to reflect against and call Java code.

A Swing example

As a first example, consider calling into the Java Swing API to create an application [1] that has

  • a frame
  • button
  • a button handler that responds with a model dialog

For starters, here is the application in plain old Java:

  //java
  import javax.swing.JFrame;
  import javax.swing.JButton;
  import javax.swing.JOptionPane;
  import java.awt.event.ActionListener;
  import java.awt.event.ActionEvent;

  public class Swing {
    public static void main(String[] args) {
      JFrame frame = new JFrame("Hello Swing");
      JButton button = new JButton("Click Me");

      button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent event) {
          JOptionPane.showMessageDialog(null,
              String.format("<html>Hello from <b>Java</b><br/>" +
                            "Button %s pressed", event.getActionCommand()));
        }
      });
      frame.getContentPane().add(button);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setVisible(true);
    }
  }

Below, I will present the same Swing application, ported to the Java.next languages. Please take note of two things about these examples:

  • For this post, I am presenting the languages in order of increasing syntactic distance from Java. This makes sense for porting a simple example from the well-known to the increasingly unfamiliar.
  • The ports below are not best practice in the Java.next languages. They are deliberately simplistic, so that I can focus on Java interop. In later installments of this series I will show more idiomatic Java.next code.

Groovy Swing example

Groovy is the Java.next language that looks most like Java. Here is the same example in Groovy:

  //groovy
  import javax.swing.JFrame
  import javax.swing.JButton
  import javax.swing.JOptionPane
  import java.awt.event.ActionListener

  frame = new JFrame("Hello Swing")
  button = new JButton("Click Me")

  button.addActionListener({
    JOptionPane.showMessageDialog(null, """<html>Hello from <b>Groovy</b>
  Button ${it.actionCommand} pressed""")
  } as ActionListener) 
  frame.contentPane.add button

  frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
  frame.pack()
  frame.visible = true

If you compare this to the Java example, it is almost the same code, minus a bunch of unnecessary ceremony. The Groovy version lets us omit:

  • semicolons
  • type declarations
  • most parentheses
  • get and set for property access

The most important benefit, however, comes in the action listener. The Groovy version sports

  • a multiline string (delimited by """)
  • string interpolation of it.actionCommand (inside ${})
  • no need to write an anonymous inner class, simply pass an anonymous function

For a more idiomatic approach to Swing in Groovy, see the Groovy SwingBuilder project.

Since this post is about Java interop I will state the obvious: From Groovy, Java interop is entirely trivial.

Scala Swing example

Next, let's look at the Scala version:

  // Scala (almost right, see below)
  import javax.swing._
  import java.awt.event.{ActionEvent, ActionListener}

  object HelloWorld extends JFrame("Hello Swing") {
    def showButtonMessage(msg: String)  =
      JOptionPane.showMessageDialog(null, String.format("""<html>Hello from <b>Scala</b>. Button %s pressed""", Array(msg)));

    def main(args: Array[String]) {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
      val button = new JButton("Click Me")
      button.addActionListener((e:ActionEvent) => showButtonMessage(e.getActionCommand.toString))
      getContentPane add button
      pack
      setVisible(true)
    }
  }     

The Scala version offers many of the same advantages over Java that the Groovy version provided:

  • fewer type declarations than Java
  • fewer semicolons
  • fewer parentheses

We also see a few items unique to Scala:

  • In Scala, the import wildcard is _, not *. In Scala, * is a valid identifier. (Scala's punctuation-friendly identifiers will be a big advantage later when I am writing DSLs.)
  • Scala has an inline syntax for importing multiple classes in a package.
  • Since we only need one, we declare an object instead of a class.
  • Our object extends JFrame, and Scala lets us call the JFrame constructor inline, instead of having to declare a separate constructor.

Again, the most important differences are in the action listener. Like Groovy, Scala lets us skip the anonymous inner class ritual, and simply pass a function:

  button.addActionListener((e:ActionEvent) => 
    showButtonMessage(e.getActionCommand.toString))

That looks great, except I cheated a little. Scala's implementation of strong typing won't automatically coerce a function into an ActionListener, so the above code won't compile out of the box. Fortunately, Scala's implicit conversions let us have our cake and eat it too: strong typing plus much of the syntactic convenience of a looser type system. All we have to do is tell Scala the the conversion is legal:

  // Yes, we can
  implicit def actionPerformedWrapper(func: (ActionEvent) => Unit) = 
    new ActionListener { def actionPerformed(e:ActionEvent) = func(e) }

With this one-time setup in place, we can now pass a function where an ActionListener is expected.

There seem to be several projects to wrap Swing in more idiomatic Scala. Using one of these libraries you should be able to get a syntax cleaner than the sample code here. See ScalaGUI for one example.

From Scala, Java interop is trivial.

JRuby Swing example

Let's see how JRuby fares:

  include Java
  import javax.swing.JFrame
  import javax.swing.JButton
  import javax.swing.JOptionPane
  import java.awt.event.ActionListener

  button = JButton.new "Click Me"
  button.add_action_listener do |evt|
    JOptionPane.showMessageDialog(nil, <<-END)
  <html>Hello from <b>JRuby</b>.
  Button '#{evt.getActionCommand()}' clicked.
  END
  end

  frame = JFrame.new "Hello Swing"
  frame.content_pane.add button
  frame.default_close_operation = JFrame::EXIT_ON_CLOSE
  frame.pack
  frame.visible = true

If you compare this to the earlier Groovy example, you will see almost exactly the same feature set:

  • fewer type declarations
  • fewer semicolons
  • fewer parentheses
  • simplified property access (no get or set)
  • a multiline string (delimited by END)
  • string interpolation of evt.getActionCommand (the stuff inside #{})

The action listener callback is simplified in a fashion similar to the Groovy example. Ruby automatically generates the ActionListener from a block:

  button.add_action_listener { |evt|
    # do stuff
  }

In the JRuby example I used Ruby conventions for method names, even on Java objects:

  # Ruby
  frame.content_pane

Java programmers expect camel case. As a convenience, JRuby supports both naming conventions:

  # Groovy, Scala, or JRuby
  frame.contentPane

Ruby's flexibility has encouraged a lot of experimentation with alternate syntaxes for Java interop. See JRUBY-903 for some of the history. For a more idiomatic approach to Swing in JRuby, see the Profligacy project.

From JRuby, Java interop is trivial.

Clojure Swing example

Here is the Clojure version:

  ; Clojure 
  ; Clojure
  (import '(javax.swing JFrame JButton JOptionPane))
  (import '(java.awt.event ActionListener))

  (let [frame (JFrame. "Hello Swing")
       button (JButton. "Click Me")]
   (.addActionListener button
     (proxy [ActionListener] []
       (actionPerformed [evt]
         (JOptionPane/showMessageDialog  nil,
            (str "<html>Hello from <b>Clojure</b>. Button "
                 (.getActionCommand evt) " clicked.")))))

   (.. frame getContentPane (add button))
   (doto frame
     (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
     .pack
     (.setVisible true)))

Because Clojure is a Lisp, the syntax is radically different from the others. This deserves hours of discussion, or none. Since my focus here is on Java interop, I am going to save The Great Parenthesis Debate for a later entry in this series. For now, let us suspend judgment on syntax, and focus exclusively on the Java interop.

Importing Java classes is easy. import takes a list. The first element of the list is a package, and the remaining elements are classes to add to the current namespace. Note that this allows the import of multiple classes in a single line.

  (import '(javax.swing JFrame JButton JOptionPane))

Creating a Java instance is easy. Use the (class. &args) form.

  (JFrame. "Hello Swing")

There are multiple ways to call methods on a Java class. If you want to call a single method, you can use the (.methodName obj &args) form. For static calls, you can also use the (class/method &args) form:

  (JOptionPane/showMessageDialog nil "A message") 

Sometimes you want to chain multiple calls together. Where in Java you would say x.y().z(), in Clojure you can use the (.. x (y) (z)) form.

  (.. frame (getContentPane) (add button))

The last three method calls in our example are all on the same frame object. With Clojure's doto form, you can perform multiple operations on an object without having to repeat the object each time.

  (doto frame
    (setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
    pack
    (setVisible true)))

As with the other examples, the action listener is the most interesting part. In Clojure, proxy will dynamically create a Java instance [2], allowing you to implement interfaces and methods as needed.

  (proxy [ActionListener] []
    (actionPerformed [evt] {do stuff here...}))

As with JRuby, this solution is more general, and requires more syntax, than the Groovy approach. Also as with JRuby, you can easily roll your own syntax.

From Clojure, Java interop is trivial.

Try this at home

The interop story in Java.next is almost boring: It Just Works. So to spice things up a little, here is an exercise in rolling your own constructs, inspired by the examples above. Consider Clojure's import, which can import multiple Java classes in a single line of code.

  (import '(javax.swing JFrame JButton JOptionPane))

Why can't this be even more general? Try your hand at writing a custom import function in one of the Java.next languages. Some useful features might be

  • import all the classes in a JAR
  • import all the classes in the intersection of a package and a JAR
  • import only interfaces
  • import all classes matching some criteria
  • import all classes except those matching some criteria

Let me know what you come up with, and I will link to it here.

Conclusion

In the examples above, I have demonstrated how all of the Java.next libraries can trivially interoperate with Java. Each of examples called the Swing library with fewer lines of code than the Java version. More importantly, the Java.next versions capture the essence of the program with less ceremony.

Seamless interoperation with Java should not be the primary yardstick when measuring Java.next languages, because they all get it right. There are complexities and corner cases beyond what I have shown here, in all of the Java.next languages. But I consider the Java interop problem to be basically solved.

In these first two articles, I have stayed fairly close to Java style while demonstrating Java.next language features. With that groundwork in place, it is time to start using idiomatic Java.next. In the next installment of the Java.next series, we will look at how the Java.next languages support Domain-Specific Languages.


Notes

  • This series is taken from the JVM Language Shootout talk. Check the schedule for a talk near you.
  • Suggestions for improving the code samples above are most welcome.
  • Thanks to Jason Rudolph, Glenn Vanderburg, and Greg Vaughn for reading an earlier draft of this article.

Footnotes

  1. I took the Swing application example from the JRuby samples, and ported it to the other Java.next languages.
  2. Clojure's proxy creates classes as necessary behind the scenes. In Java.next, the dichotomy of class and object is not constantly center stage.

Revisions

  • 2008/08/14. Updated Clojure example and prose per Rich Hickey's suggestion. Updated Groovy example to include pointer to SwingBuilder, per Andres Almiray. Updated JRuby example and prose based on suggestions from Nick Sieger and Ola Bini. Updated Scala example per Tony Morris's suggestion. Thanks for all the improvements!
  • 2009/10/16. Update Clojure example to the modern (dot-requiring) version of doto.
Get In Touch