
-----------------------------------
wtd
Sat Dec 03, 2005 7:36 pm

Scala and Java: A Quick Swing Comparison by Example
-----------------------------------
To be equitable, Java syntax highlighting is not used.

More on the interesting features of Scala (as seen here) in a bit.

Java:

import javax.swing.*;
import java.awt.event.*;
import static javax.swing.JFrame.*;

class MyWindow extends JFrame {
   public MyWindow() {
      super("Hello World");

      setDefaultLookAndFeelDecorated(true);

      setDefaultCloseOperation(EXIT_ON_CLOSE);

      JButton button = new JButton("Hello World");
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            System.out.println("Hello world");
         }
      });
      getContentPane().add(button);

      pack();
      setSize(400, 300);
      setVisible(true);
   }
}

public class HelloWorldSwing {
   public static void main(String[] args) {
      MyWindow win = new MyWindow();
   }
}

Scala:

import javax.swing._;

class MyWindow2 extends JFrame("Hello World") {
   import JFrame._;

   setDefaultLookAndFeelDecorated(true);

   setDefaultCloseOperation(EXIT_ON_CLOSE);

   val button = new JButton("Hello world");
   button addActionListener Print("Hello world");
   getContentPane() add button;

   pack;
   setSize(400, 300);
   setVisible(true);
   
   import java.awt.event._;
   
   case class Print(msg : String)
    with ActionListener
    with Application {
      def actionPerformed(e : ActionEvent) = 
         Console println msg
   }
}

object HelloWorldScala with Application {
   val win = new MyWindow2
}

-----------------------------------
wtd
Sun Dec 04, 2005 3:17 pm


-----------------------------------
Noteworthy differentiations from Java, in no particular order:


Import difference #1

import javax.swing.*;

vs.

import javax.swing._;

* is a valid identifier in Scala, and thus cannot be used the same way it is in Java.  The used of _ does fit with how underscores are used in pattern matching, which is a nice bit of consistency.

Import difference #2: static imports and relativism

import static javax.swing.JFrame.*;

vs.

import JFrame._;

Static imports in Scala are less verbose and clearer than their Java counterparts.  

Imports in general are also relative in Scala.  In Java, the JFrame class had to be fully qualified.  In Scala, we can simply specify the JFrame class, because it tries to look in javax.swing first.

Import difference #3: local imports

import java.awt.event.*;

vs.

import java.awt.event._;

In the Java program, we had to place this import at the top of the program, and import the names in java.awt.event into everything in this file.

In Scala, we used a local import.  

Parameterized classes

class MyWindow extends JFrame {
   public MyWindow() {
      super("Hello World");
      
vs.

class MyWindow2 extends JFrame("Hello World")

Scala permits paramaterized classes, rather than having constructors in the Java sense.  This makes a lot of sense, since it reduces repeated code.  A contrived example:

class Foo {
   int bar;
   double baz;
   
   public Foo(int initBar, double initBaz) {
      bar = initBar;
      baz = initBaz;
   }
}

vs.

class Foo(initBar : int, initBaz : double) {
   var bar = initBar;
   var baz = initBaz;
}

Parameterized classes are a familiar site to anyone who has worked with Objective-Caml.

Local type inferencing

JButton button = new JButton("Hello World");

vs.

val button = new JButton("Hello world");

Local type inferencing is visible here.  Why specify the type of "button"?  "new JButton" does it quite well.

Additionally, "val" rather than "var" serves to make the variable "final".

Inner classes and magical infix operators from methods

      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            System.out.println("Hello world");
         }
      });
      
vs.
      
button addActionListener Print("Hello world");  

   case class Print(msg : String)
    with ActionListener
    with Application {
      def actionPerformed(e : ActionEvent) = 
         Console println msg
   }

Scala does not support anonymous inner classes.  It does however support inner classes.

In this case we've made it a "case class", which removes the need to use "new".

The Print class mixes in ActionListener and Application.  From ActionListener we get the obligation to implement actionPerformed, and from Application we get access to Console.  Print of course is parameterized with the string that we actually want it to print, so that we can reuse this class.

Visible in this code snippet is the use of Scala's ability to turn methods into infix operators.

Console println msg

Can also be called as:

Console.println(msg)

Also visible is a reduction of braces and semi-colons.

Since actionPerformed contains only one expression, it can be expressed without braces.  Additionally, since the braces for the Print class form natural punctuation, there's no need for a semi-colon following the actual println expression.

Main... or not

public class HelloWorldSwing {
   public static void main(String[] args) {
      MyWindow win = new MyWindow();
   }
}

vs.

object HelloWorldScala with Application {
   val win = new MyWindow2
}

Objects in Scala represent an alternative way of organizing variables and data.  They are not instantiated like classes.  They are essentially modules.  

Here I've mixed Application into my object, meaning that I don't need a "main" method for such a simple program.

Again, local type inferencing is used to avoid repetitive typing of "MyWindow2".


-----------------------------------
wtd
Sun Dec 04, 2005 6:09 pm


-----------------------------------
Modified examples, where the button is a subclass of JButton.

Java:

import javax.swing.*;
import java.awt.event.*;
import static javax.swing.JFrame.*;

class MyButton 
 extends JButton 
 implements ActionListener {
   private String msgToPrint;

   public MyButton(String title, String toPrint) {
	  super(title);

	  msgToPrint = toPrint;
	  addActionListener(this);
   }

   public void actionPerformed(ActionEvent e) {
	  System.out.println(msgToPrint);
   }
}

class MyWindow extends JFrame {
   public MyWindow() {
      super("Hello World");

      setDefaultLookAndFeelDecorated(true);

      setDefaultCloseOperation(EXIT_ON_CLOSE);

      MyButton button = new MyButton("Hello World", "Hello world");
      getContentPane().add(button);

      pack();
      setSize(400, 300);
      setVisible(true);
   }
}

public class HelloWorldSwing {
   public static void main(String[] args) {
      MyWindow win = new MyWindow();
   }
}

Scala:

import javax.swing._;   
import java.awt.event._;

class MyButton2(title : String, msgToPrint : String) 
 extends JButton(title)
 with ActionListener
 with Application {
   addActionListener(this);
   
   def actionPerformed(e : ActionEvent) =
      Console println msgToPrint
}

class MyWindow2 extends JFrame("Hello World") {
   import JFrame._;

   setDefaultLookAndFeelDecorated(true);

   setDefaultCloseOperation(EXIT_ON_CLOSE);

   val button = new MyButton2("Hello world", "Hello world");
   getContentPane() add button;

   pack;
   setSize(400, 300);
   setVisible(true);
}

object HelloWorldScala with Application {
   val win = new MyWindow2
}

-----------------------------------
Cervantes
Sun Dec 04, 2005 10:12 pm


-----------------------------------
So wtd, what's your review of Scala, so far?  You seem to be spending a lot of time with it.

-----------------------------------
wtd
Sun Dec 04, 2005 10:25 pm


-----------------------------------
Parameterized classes

The major distainction between the two examples in the previous post boils down to:

class MyButton
 extends JButton
 implements ActionListener {
   private String msgToPrint;

   public MyButton(String title, String toPrint) {
     super(title);

     msgToPrint = toPrint;
     addActionListener(this);
   }

   public void actionPerformed(ActionEvent e) {
     System.out.println(msgToPrint);
   }
}

vs.

class MyButton2(title : String, msgToPrint : String)
 extends JButton(title)
 with ActionListener
 with Application {
   addActionListener(this);
   
   def actionPerformed(e : ActionEvent) =
      Console println msgToPrint
}

In the Java example, the MyButton class has a constructor which takes two Strings.  The first is the text to display in the button.  This it passes to the constructor for the JButton class.  The second String is the message to print to the console when the button is clicked.

In order to use this second value in the actionPerformed method, we have to assign it first to an instance variable which will still be in scope when the constructor is finished.

Scala dramatically simplifies this.  The values passed to the class become accessible to any method.

-----------------------------------
wtd
Sun Dec 04, 2005 11:14 pm


-----------------------------------
So wtd, what's your review of Scala, so far?  You seem to be spending a lot of time with it.

I rather like it.

It's not flawless, but it addresses a number of concerns I had in the Why Java Sucks thread.

-----------------------------------
wtd
Mon Dec 05, 2005 12:23 pm


-----------------------------------
A further use of mix-in composition to implement this absurdly simple program.

import javax.swing._;   
import java.awt.event._;

class PrintAction(msg : String) 
 with ActionListener { 
   def actionPerformed(e : ActionEvent) =
      Console println msg  
}

class MyButton2(title : String, msgToPrint : String) 
 extends JButton(title)
 with PrintAction(msgToPrint) {
   addActionListener(this)
}

class MyWindow2 extends JFrame("Hello World") {
   import JFrame._;

   setDefaultLookAndFeelDecorated(true);

   setDefaultCloseOperation(EXIT_ON_CLOSE);

   val button = new MyButton2("Hello world", "Hello world");
   getContentPane() add button;

   pack;
   setSize(400, 300);
   setVisible(true);
}

object HelloWorldScala with Application {
   val win = new MyWindow2
}

The actionPerformed method is here defined within the PrintAction class, and then that class is mixed into the new button class.

I've also come to learn that Scala does in fact support anonymous inner classes, so the first Scaa example I gave can be more directly translated from the Java, and can better demonstrated the brevity the language makes possible.

import javax.swing._;
import java.awt.event._;

class MyWindow2 extends JFrame("Hello World") {
   import JFrame._;

   setDefaultLookAndFeelDecorated(true);

   setDefaultCloseOperation(EXIT_ON_CLOSE);

   val button = new JButton("Hello world");
   button addActionListener new ActionListener {
      def actionPerformed(e : ActionEvent) =
         Console println "Hello world"
   };
   getContentPane() add button;

   pack;
   setSize(400, 300);
   setVisible(true);
}

object HelloWorldScala with Application {
   val win = new MyWindow2
}

-----------------------------------
wtd
Tue Dec 06, 2005 1:51 pm


-----------------------------------
In the previous post I had this code:

val button = new JButton("Hello world");
button addActionListener new ActionListener {
   def actionPerformed(e : ActionEvent) =
      Console println "Hello world"
};
getContentPane() add button

That's all well and good, and it's a pretty straightforward transition from Java, but with somewhat prettier syntax.  Let's diverge a little bit further.

getContentPane() add new JButton("Hello world") with ActionListener {
   addActionListener(this);

   def actionPerformed(e : ActionEvent) =
      Console println "Hello world"
};

-----------------------------------
wtd
Tue Dec 06, 2005 8:16 pm


-----------------------------------
New app.

Java:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class JTest {
   public static void main(String[] args) {
      JMyWindow win = new JMyWindow();
   }
}

class JMyWindow extends JFrame {
   private final JTextField inputField;
   private final JSpinner   numberOfCopies;
   private final JTextArea  displayArea;

   public JMyWindow() {
      super("JTest");

      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      inputField = new JTextField(50);
      numberOfCopies = new JSpinner(new SpinnerNumberModel(1, 1, 256, 1));
      displayArea = new JTextArea();

      inputField.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            String inputText = inputField.getText();
            int numCopies = ((Integer)numberOfCopies.getValue()).intValue();

            for (int i = 0; i < numCopies; ++i) {
               displayArea.append(inputField.getText());
            }

            displayArea.append("\n");
         }
      });

      displayArea.setEditable(false);
      displayArea.setLineWrap(true);

      Container contentPane = getContentPane();
      contentPane.setLayout(new GridBagLayout());

      GridBagConstraints c = new GridBagConstraints();

      c.gridx = 0;
      c.gridy = 0;
      c.insets = new Insets(2, 2, 2, 2);
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 10.0;
      contentPane.add(inputField, c);

      c.gridx = 1;
      c.weightx = 0.0;
      contentPane.add(new JLabel("Copies"), c);

      c.gridx = 2;
      c.weightx = 0.0;
      contentPane.add(numberOfCopies, c);

      c.gridx = 0;
      c.gridy = 1;
      c.gridwidth = 3;
      c.weightx = 10.0;
      c.weighty = 10.0;
      contentPane.add(new JScrollPane(displayArea), c);

      pack();
      setSize(300, 400);
      setVisible(true);
   }
}

Scala:

import javax.swing._;
import java.awt._;
import java.awt.event._;

object STest with Application {
   val win = new SMyWindow
}

class SMyWindow extends JFrame("STest") {
   setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

   private val inputField = new JTextField with ActionListener {
      addActionListener(this);
   
      def actionPerformed(e : ActionEvent) = {
         val inputText = getText();
         val numCopies = numberOfCopies.getValue().asInstanceOf[java.lang.Integer].intValue();
         
         for (val i 