Clever Java Things

We've covered a lot this semester. Today's topic is about a couple miscellaneous Java topics that didn't quite fit anywhere else, but of which every technologically adept CS/IT student should know.

Anonymous Classes

We've created a lot of classes so far, and for the most part, that has involved creating an entirely new file for each new class. This makes sense for class definitions from which you will create multiple objects, and when you need that new class type across various methods. However, sometimes you just want a quick object that will be used only one time, and it is tedious to write new class definitions for every little class. It also could lead to an explosion of files!

An anonymous class is a class that has no name which extends an already existing class or simply implements an interface. You both define it and instantiate an instance of it at the same time. Let's start with an example:

JButton button = new JButton();
button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    System.out.println("You clicked me!");
  }
});

This should look strange to you. An ActionListener is an interface, so writing new ActionListener() should immediately set off alarms in your head. You can't create an instance of an interface! The difference here is that the new expression is followed by a block of code which you should interpret as a class definition. We define an actionPerformed method because that's what an ActionListener requires, so we've fulfilled the interface's requirements. We've thus defined a brand new class which implements ActionListener. It has no name (anonymous) and it is defined solely to be a single object right here in the code.

Try this Good.java program out yourself!

import javax.swing.*;
import java.awt.event.*;

public class Good {
  public static void main(String[] args) {
    JFrame f = new JFrame("Button Click!");

    JButton button = new JButton("click me");
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("You clicked me!");
        }
      });

    f.add(button);
    f.pack();
    f.setVisible(true);
  }
}

Hopefully you see the benefit of anonymous classes. I needed a single listener for this single button, so I just defined it right then and there. Done! You can add as many methods as you need to your code block, with the exception of constructors. Anonymous classes do not have constructors which makes sense because nobody else can create an instance of it by virtue of your inline declaration.

Let's make this example slightly more interesting. We won't just print a message when clicked, but will also track the number of clicks with a class variable. Also, instead of creating the button in main(), let's create our own class that extends JFrame and contains the JButton inside it. Our main method will just be in charge of listening for that JFrame to close. Spend some time understanding the following code, as it creates two anonymous classes: (1) extends the class WindowAdapter, and (2) implements the interface ActionListener.

Good.javaMyGUI.java
import javax.swing.*;
import java.awt.event.*;

public class Good {

  public static void main(String[] args) {
    MyGUI f = new MyGUI();
    f.initialize();

    // Add a window listener by extending WindowAdapter right here.
    f.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.out.println("Clicked a total of " + f.getCount() + " times!");
          System.exit(0);
        }
      });
  }
                  }
import javax.swing.*;
import java.awt.event.*;

public class MyGUI extends JFrame {
  private int counter = 0;

  public void initialize() {
    // Create the button and add the listener as an anonymous class.
    JButton button = new JButton("click me");
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          counter++;
          System.out.println("Clicked: " + counter);
        }
      });

    // Add the button to the JFrame and set visible!
    this.add(button);
    this.pack();
    this.setVisible(true);
  }

  public int getCount() { return counter; }

}

We could say a lot more about anonymous classes, but you are now at the point where you can understand official documentation and instructional materials from Oracle (and other sources, of course). Consider this a small chapter of reading, as if we made you buy a textbook for this class: read Oracle's brief tutorial on anonymous classes.


Lambda Expressions

Very much related to the anonymous class is the lambda expression (also new in the Java 8 release!). Lambda expressions are similar to anonymous classes but are only used when there is only one method in the interface that you are implementing. For example, the ActionListener in our code above has just one required method actionPerformed(). The anonymous class above has to spell out the entire actionPerformed method and code block, but lambda expressions skip that:

button.addActionListener( (ev) -> System.out.println("Clicked!") );

Weird, right? Not weird! If the interface has just one method, then the compiler knows exactly which one you must implement. As long as you give the correct parameters to that method's signature, you're good to go! Let's talk about the syntax of a lambda expression:

(param1, param2, ..., paramN) -> { expression1; expression2; ...; expressionM }

The parameters to the interface's method are on the left, and the code block is on the right. The curly braces are also optional if you just have one expression to run. In fact, the parentheses on the left-hand side are optional too if there is just a single parameter! Lambda expressions come from mathematical logic where you bind variables to values (left-hand side), and those values fill in the variables in an expression (right-hand side). In Java, this is yet another shorthand for an anonymous class with just one method. You might learn about functional programming languages in your later major courses which make much more use of lambda expressions.

Ok, so why do we care? Well it's partly just neat, but it's mostly very convenient and speeds up your coding. Let's continue our anonymous class example from above but instead use a lambda expression:

import javax.swing.*;
import java.awt.event.*;

public class MyGUI extends JFrame {
  private int counter = 0;

  public void initialize() {
    // Create the button and add the listener as an anonymous class.
    JButton button = new JButton("click me");
    button.addActionListener( (ev) -> { counter++; System.out.println("Clicked: " + counter); } );

    // Add the button to the JFrame and set visible!
    this.add(button);
    this.pack();
    this.setVisible(true);
  }

  public int getCount() { return counter; }

}

The variable ev is the lambda variable, and the compiler sets its type to be an ActionEvent because that's what the actionPerformed(ActionEvent) method in the ActionListener interface requires! Lambda expressions are neat because you can save them as variable types too. If you think this example was too abbreviated, you could expand it this way:

ActionListener listen = (ev) -> { counter++; System.out.println("Clicked: " + counter); };
button.addActionListener(listen);

We've come a long way! Look at that listen variable! Before this lecture, you would have had to make your own custom class (probably in a new file) that implemented ActionListener. We then showed you how to shorten it with an anonymous class, and now we've turned it into a one-liner with a lambda expression.

You can do a lot more with lambda expressions, and we encourage you to spend time with this great tutorial or other resource online.


Distributable Java programs: jar files

A runnable Java program is typically defined by a collection of .class files. You "run" the program by executing the JVM with the name of the class whose main() method is supposed to kick off the program, and as the JVM encounters references to classes it doesn't know about, it looks around for the .class files for those classes, loads them, and continues. It would be quite painful to distribute even relatively small Java programs if it had to be done in this form. The instructions would look like "Download and save the following 27 .class file ...". To get around this, Java has a file format called .jar files, that package many .class files into a single file. If you have a .jar file called Foo.jar, you can run the program it contains with the command:

java -jar Foo.jar

The one thing that's not clear, however, is what class has the main() method that should start things off. After all, many .class files could contain main() methods. The way this is handled is that a file called Manifest.txt is bundled together with all the .class files in the jar file. And Manifest.txt has a line that states which class has the main() method that should be called to kick off the program. Let's pretend we have a program called "L12". We would then create a jar file by first creating the file Manifest.txt:

Manifest.txt
Main-Class: L12

... and then giving the following command:

jar -cfm SolarSystem.jar Manifest.txt *.class

You would presumably have a L12.class file along with other .class files in your directory. This command creates the file SolarSystem.jar that we can send off to other people, who can run it (on any platform with a JVM) with the command:

java -jar SolarSystem.jar

Since the "compiled" code is byte-code compiled for the JVM, rather than true machine-code, it can be run anywhere ... provided the host platform supports the version of Java you compiled to.

For your viewing pleasure, I've included SolarSystemA.jar and SolarSystemC.jar for you to run. Note: beware of running .jar files when you're not absolutely sure that a) the person who wrote it is on the up and up, and b) that you're really gotten the file you think you have!