Sunday, September 20, 2015

Displaying images from files in JavaFX

As I continue to both employ JavaFX in my own code and teach JavaFX in my courses, I am slowly learning how to perform tasks I used to do in Swing in new ways.  The latest example is the all-important task of opening image files.

The strategy employed in this example is to set the image file as a background for a JavaFX Pane object.  Opening the image is a straightforward use of the JavaFX file chooser dialog.  Error handling is done via a simple error dialog created as an Alert.  Displaying the image requires the following steps:

  • Transform the File object into a URL, and use this to create an Image object.
  • Create a BackgroundSize for the object.  
    • By setting the height and width to AUTO, the image will automatically resize as the window displaying it is resized. 
    • It will not deform the image; if the aspect ratio of the displaying window does not match the image, the image will be truncated.
  • Create a BackgroundImage object.
    • The first parameter passes the Image object to use as its basis.
    • The second and third parameters control whether the object is repeated in the X and Y directions.  For this example, I deemed this undesirable.
    • The fourth parameter centers the image in the background.
    • The final parameter is the BackgroundSize object created before. 
  • Finally, we designate the BackgroundImage as the Pane's background.

import java.io.File;
import java.net.MalformedURLException;

import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.Pane;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;

public class DemoController {
 @FXML
 Pane pane;
 
 @FXML
 Button open;
 
 @FXML
 void initialize() {
  open.setOnAction(event -> imageOpener());
 }
 
 void imageOpener() {
  File imgFile = openImage();
  try {
   if (imgFile != null) {
    displayImage(imgFile);
   } 
  } catch (MalformedURLException e) {
   showError(e);
  }
 }
 
 File openImage() {
  FileChooser fileChooser = new FileChooser();
  fileChooser.setTitle("Select an Image File");
  fileChooser.getExtensionFilters().add(new ExtensionFilter(
    "Image Files", "*.png", "*.jpg", "*.gif"));
  return fileChooser.showOpenDialog(null);
 }
 
 void displayImage(File imgFile) throws MalformedURLException {
  Image background = new Image(imgFile.toURI().toURL().toString());
  BackgroundSize size = new BackgroundSize(BackgroundSize.AUTO,
    BackgroundSize.AUTO, false, false, true, true);
  BackgroundImage bimg = new BackgroundImage(background,
    BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT,
    BackgroundPosition.CENTER, size);
  pane.setBackground(new Background(bimg));
 }
 
 void showError(Exception e) {
  Alert info = new Alert(AlertType.ERROR);
  info.setTitle("Image not found");
  info.setHeaderText("Image not found");
  info.setContentText(e.getMessage());
  info.showAndWait();
 }
}

Dragging objects with a mouse in JavaFX

Dragging objects is pretty straightforward in JavaFX as long as some key concepts are kept straight.  If we go with the strategy of attaching the event handler to the object to be dragged, then the very first time that the onMouseDragged event is invoked, the (x,y) coordinates given by the event parameter will be the location on the object that was clicked.

If we then move the object, on subsequent invocations of the event as a result of moving the mouse, the (x,y) coordinates will be the relative distance between the mouse position at the time of the preceding event and the current mouse position.  This works because the new position of the object is determined by the previous mouse location.

In either case, it works pretty well to use the (x,y) coordinate of the mouse as an offset by which to translate the object.  The code below demonstrates the concept nicely, provided that a suitable GUI has been created using SceneBuilder and a suitable main() has also been created.  The drag() method is set up to work properly with a draggable Node of any subtype.

import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;

public class DemoController {
 @FXML
 Pane pane;
 
 @FXML
 Circle ball1, ball2;
 
 @FXML
 void initialize() {
  ball1.setOnMouseDragged(event -> drag(event));
  ball2.setOnMouseDragged(event -> drag(event));
 }
 
 public void drag(MouseEvent event) {
  Node n = (Node)event.getSource();
  n.setTranslateX(n.getTranslateX() + event.getX());
  n.setTranslateY(n.getTranslateY() + event.getY());
 }
}

Tuesday, September 8, 2015

Be careful with operator precedence

I recently encountered the following bug while programming in Java. I've taken it out of context and created the program below for illustrative purposes.

The goal is to add two byte values while avoiding overflow. To this end, we cast them to integers, and we use bit-masking to avoid sign extension when casting.  The unsuccessful version is highlighted in red, while the successful version is green:

public class ConvertBytes {
 public static void main(String[] args) {
  byte a = 127;
  byte b = 126;
  int bad = ((int)a) & 0xff + ((int)b) & 0xff;
  System.out.println(bad);
  int good = ((int)a & 0xff) + ((int)b & 0xff);
  System.out.println(good);
 }
}

When this program is executed, bad is 125 and good is 253.

I can't believe that after 13 years as an active Java programmer (with 11 years before that as an active C++ programmer) I made so elementary a mistake, but I'm posting it here in hopes it will be a useful warning to others.  For whatever reason, I assumed that bitwise-and had a higher precedence than addition, perhaps because it is a traditionally "multiplicative" operator.  As it turns out, its precedence is actually much lower.

A further observation is that this is not what I thought the bug was originally.  At first, I thought I had somehow messed up the casting.  It was only after I rechecked it carefully that I realized I was killing the higher-order bits with the bitwise and.

Thursday, May 28, 2015

Handling Keyboard Events in JavaFX

When using JavaFX, SceneBuilder is not terribly helpful when setting up keyboard events.  This is because SceneBuilder's event handlers can't take any parameters.  Consequently, there is no way to determine which key triggered the event.

I've made an amusing little program that has four triangles on the GUI, one for each arrow key.  When an arrow key is pressed, the corresponding triangle changes color.  When it is released, it switches back to the original color.  Here is the source code for its controller; constructing the rest of the GUI is an exercise for the reader.

package application;

import java.util.Map;
import java.util.TreeMap;

import javafx.fxml.FXML;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;

public class ButtonPaneController {
 @FXML
 Polygon up;
 @FXML
 Polygon down;
 @FXML
 Polygon left;
 @FXML
 Polygon right;
 @FXML
 AnchorPane pane;
 
 Color pressColor = Color.GOLD;
 Color releaseColor = Color.DODGERBLUE;
 
 Map<KeyCode,Polygon> key2poly;
 
 @FXML
 void initialize() {
  pane.setOnKeyPressed(key -> recolor(key.getCode(), 
    pressColor));
  pane.setOnKeyReleased(key -> recolor(key.getCode(), 
    releaseColor));
  pane.setOnMouseEntered(m -> pane.requestFocus());
  key2poly = new TreeMap<>();
  key2poly.put(KeyCode.LEFT, left);
  key2poly.put(KeyCode.RIGHT, right);
  key2poly.put(KeyCode.UP, up);
  key2poly.put(KeyCode.DOWN, down);
 }
 
 void recolor(KeyCode code, Color newColor) {
  if (key2poly.containsKey(code)) {
   key2poly.get(code).setFill(newColor);
  }
  pane.requestFocus();
 }
}

In the initialize() method, we set up our event handlers using lambdas. Whenever a key is pressed, recolor() is called with the key code and the target color upon a press.  Similarly, whenever a key is released, the same method call is made, but with a different target color.  Finally, we request the focus whenever the mouse enters the pane. This could easily have been set up in SceneBuilder, but I did it in code here for consistency.

Having set up the handlers, I use a Map to simplify the implementation.  By mapping each key to the corresponding GUI element that is to have its color changed, I avoid a bothersome set of if-else clauses in the recolor() method.  A key-based event handler with more substantive algorithmic content could use a Map in a similar way where the values are lambda expressions.

Monday, May 18, 2015

Using the leJOS-0.9 video library

The following program illustrates how one might use the LeJOS-0.9 webcam library in conjunction with the lambda feature from Java 8:

package edu.hendrix.demo;

import java.io.IOException;
import java.util.function.Consumer;

import lejos.hardware.BrickFinder;
import lejos.hardware.Button;
import lejos.hardware.lcd.GraphicsLCD;
import lejos.hardware.video.Video;
import lejos.hardware.video.YUYVImage;

public class FilteredCameraDemo {
  private Consumer<YUYVimage> imgConsumer;
 
  public FilteredCameraDemo(Consumer<YUYVimage> filterer) {
    imgConsumer = filterer;
  }

  public void run() {
    try {
      Video wc = BrickFinder.getDefault().getVideo();
      wc.open(160,120);
      byte[] frame = wc.createFrame();
      YUYVImage img = 
        new YUYVImage(frame, wc.getWidth(), wc.getHeight());
      while (!Button.ESCAPE.isDown()) {
        wc.grabFrame(frame);
        imgConsumer.accept(img);
      }
      wc.close();
    } catch (IOException ioe) {
      ioe.printStackTrace();
      System.out.println("Driver exception: " + ioe.getMessage());
    }
  }
 
  public static void main(String[] args) {
    GraphicsLCD g = BrickFinder.getDefault().getGraphicsLCD();
    new FilteredCameraDemo
      (img -> img.display(g, 0, 0, img.getMeanY())).run();
  }
}

To allow flexibility of implementation, the YUYVImage class is essentially a wrapper around a byte array.  So every time we grab a frame from the webcam, we dump the bytes into a byte array that, in turn, we access via a YUYVImage that maintains a reference to it.  This YUYVImage class has methods that decode the byte layout according to the YUYV standard.

We have a private data member belonging to the Consumer interface. Consumer objects have an accept() method that takes a parameter (corresponding to the parameterized type) and does not return anything.  We make use of this within the run() method's while loop.  In this case, the type parameter is a YUYVImage object.

Now, when we invoke the constructor, we can write a lambda expression to describe the desired computation.  For this example, we just display the image.  Note that the LCD screen is referenced from outside the scope of the lambda.  This still works after it has gone out of scope, as Java 8's lambda expressions are closures.  You can write code to do arbitrary things when seeing an image, just by calling the FilteredCameraDemo constructor with a different lambda parameter.

I plan to write a more elaborate example in a future blog post, but this should suffice to demonstrate the concept.  Happy coding!

Setting up leJOS-0.9 with Java 8

The premier implementation of Java for the various Lego Mindstorms robots is LeJOS.  For the Mindstorms EV3, Oracle has facilitated the LeJOS implementation by providing pre-bundled Java microeditions.  Java version 7 update 60 is ready-to-go as part of the leJOS setup.  But the Oracle download page includes the following cryptic message regarding the use of Java 8:
Java SE Embedded 8 enables developers to create customized JREs using the JRECreate tool. Starting with Java SE Embedded 8, individual JRE downloads for embedded platforms are no longer provided. To get started, download the bundle below and follow instructions to create a JRE that suits your application's needs.
Of course, for a leJOS developer who would like to use Java 8, this is not entirely helpful.  Following some hints on the LeJOS EV3 forums, I was able to figure out how to do this.  This sequence of shell commands should work on any Unix/Linux platform.  (I am using a Mac.)  I would imagine it would also work on Windows if gzip and tar programs are installed.

gunzip ejdk-8-fcs-b132-linux-arm-sflt-03_mar_2014.gz
tar xvf ejdk-8-fcs-b132-linux-arm-sflt-03_mar_2014
cd ejdk1.8.0/bin
export JAVA_HOME=/usr
./jrecreate.sh --dest ../../ejre-8u1-linux-arm-15_may_2015 --profile compact2 --vm client
cd ../..
tar cvf ejre-8u1-linux-arm-15_may_2015.tar ejre-8u1-linux-arm-15_may_2015
gzip ejre-8u1-linux-arm-15_may_2015.tar

Having created the Java 8 configuration file ejre-8u1-linux-arm-15_may_2015.tar.gz, simply specify it when creating the SD card, and everything should work fine.

The English-language summary of the above command sequence is as follows:

  • Uncompress the archive using gunzip.
  • Extract the contents of the archive using tar.
  • Go to the subdirectory containing jrecreate.
    • On Windows, you would want to use jrecreate.bat rather than jrecreate.sh.
  • Set up the JAVA_HOME environment variable if it is not already set.
  • Run jrecreate with the following options:
    • --dest specifies where the configured implementation will be placed.  
    • --profile needs to be compact2 to ensure certain features LeJOS requires are present.
    • --vm needs to be client for the same basic reason.
  • Use tar to repack the archive.
  • Use gzip to compress the archive.
  • I made up a filename that seems to be compatible with what LeJOS will be looking for.
In my next post, I'll give an example that illustrates some of the capabilities of Java 8 in this environment.

Monday, January 26, 2015

Dynamically adding JavaFX controls

The JavaFX SceneBuilder works very well for constructing a static GUI; that is, a GUI whose controls are determined entirely at compile-time.  But sometimes it is useful to be able to add new controls dynamically at run-time.  The example below demonstrates one way to do this.

In this example, the user creates Button objects on-the-fly.  They are displayed using a ListViewer and stored in an ObservableList.  Each button inverts the capitalization of its label when clicked.  The Controller class is given below; constructing the rest of the GUI is an exercise for the reader.

package application;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;

public class Controller {
    @FXML
    private Button add;
    @FXML 
    private TextField button;
    @FXML 
    private ListView<Button> visibleList;
 
    private ObservableList<Button> buttons = 
        FXCollections.observableArrayList();
 
    @FXML
    protected void initialize() {
        visibleList.setItems(buttons);
    }
 
    @FXML
    void addName() {
        Button b = new Button(button.getText());
        buttons.add(b);
        b.addEventHandler(MouseEvent.MOUSE_CLICKED, 
            (event -> b.setText(invertCapitals(b.getText()))));
        button.setText("");
    }
 
    public static String invertCapitals(String other) {
        return other.chars().mapToObj(Controller::flipCap)
                            .map(c -> Character.toString(c))
                            .reduce("", (s, c) -> s + c);
    }
 
    public static Character flipCap(int c) {
        if (c >= 'A' && c <= 'Z') {
           return (char)(c - 'A' + 'a');
        } else if (c >= 'a' && c <= 'z') {
           return (char)(c - 'a' + 'A');
        } else {
           return (char)c;
        }
    }
}

Highlighted in red, the key to making this all work is the addName() method, working in combination with the ObservableList buttons.   This method creates a new Button object in response to every click of the add button.  This object is added to our ObservableList, which makes it visible in the ListViewer.  Finally, we employ a Java 8 lambda to implement the event handler.

There is a pretty long list of event types to which one might respond.  Check out the documentation for the MouseEvent and KeyEvent classes to get started.

Here's a sample run of the program:

Java 8: Processing character streams

A common operation in a functional language like Haskell involves doing some processing on every character in a string.  For example:

Prelude Data.Char> let flip c = if (isUpper c) then (toLower c) else (toUpper c) 
Prelude Data.Char> map flip "This is a TEST"
"tHIS IS A test"

Using the Java 8 stream libraries to do a similar task is a little tricky:

public class StringDemo1 {
  public static String invertCapitals(String other) {
    return other.chars()
      .mapToObj(StringDemo1::flipCap)
      .map(c -> Character.toString(c))
      .reduce("", (s, c) -> s + c);
  }
 
  public static Character flipCap(int c) {
    if (c >= 'A' && c <= 'Z') {
       return (char)(c - 'A' + 'a');
    } else if (c >= 'a' && c <= 'z') {
       return (char)(c - 'a' + 'A');
    } else {
       return (char)c;
    }
  }
}

First, we need to convert the string to a stream. That is what the chars() method does. Unfortunately, it creates an IntStream. We use the mapToObj() method to turn the IntStream into a Stream<Character>. Having done this, we use map() to turn it into a Stream<String>, and finally we can use reduce() to combine it all into a single string.

While this does get the job done, it is very inefficient, as a new String object must be allocated for each reduction.  The following variation uses collect() to use a StringBuilder to accumulate the new String efficiently:

public class StringDemo2 {
  public static String invertCapitals(String other) {
    return other.chars()
      .mapToObj(StringDemo1::flipCap)
      .map(c -> Character.toString(c))
      .collect(StringBuilder::new,StringBuilder::append,StringBuilder::append)
      .toString();
  }
}


Using collect() is arguably not as aesthetically pleasing as reduce().  Here is an explanation of the arguments:
  • The first argument generates the collection that will be the accumulation target.
  • The second argument appends an element to the collection.
  • The third argument joins two collections.
This particular example is odd because StringBuilder::append is an overloaded static method.  The first one appends a String; the second one appends a CharSequence, an interface that StringBuilder implements.

Having compared the aesthetics, what about performance?

I found that the version with collect() could process a 100,000 character string in 19 milliseconds, while the version with reduce() requires 6520 milliseconds.

My test program is below.  It provides a nice demonstration of passing functions as parameter values.

import java.util.function.Function;
import java.util.stream.IntStream;

public class StringDemoComparison {
  public static void main(String[] args) {
    String input = 
      IntStream.iterate(1, x -> 1 + x)
               .mapToObj(x -> Character.toString((char)(x % 58 + 65)))
               .limit(100000)
               .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
               .toString();
  
    runDemo(StringDemo1::invertCapitals, input);
    runDemo(StringDemo2::invertCapitals, input);
  }
 
  public static void runDemo(Function func, String input) {
    long start = System.currentTimeMillis();
    String result = func.apply(input);
    long duration = System.currentTimeMillis() - start;
    System.out.println(result.length());
    System.out.println("Duration for: " + func.toString() + " is: " + duration);
  }
}


Friday, January 23, 2015

JavaFX ObservableList

Continuing my exploration of JavaFX, another very useful feature is its collections library.  In particular, it includes automatically observable versions of the List and Map interfaces from the Java Collections framework.  These can enable very convenient GUI updates.

For example, consider a program that allows a user to enter names into a list.  JavaFX allows us to store our data internally in an ObservableList; the GUI automatically updates whenever ObservableList changes.  Here's a screenshot of the program in action:

To create the interface in SceneBuilder:

  • Create a BorderPane as the root container.
  • Add an HBox to the top of the BorderPane.
    • Place a Button and a TextField in the HBox.
  • Add a ListView to the center of the BorderPane.
The following is the Controller class I wrote to implement the functionality:
package application;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;

public class Controller {
 @FXML
 private Button add;
 @FXML
 private TextField name;
 @FXML
 private ListView visibleList;
 
 private ObservableList names = 
                FXCollections.observableArrayList();
 
 @FXML
 private void initialize() {
  visibleList.setItems(names);
 }
 
 @FXML
 private void addName() {
  if (name.getText().length() > 0) {
   names.add(name.getText());
   name.setText("");
  }
 }
}

In SceneBuilder:

  • Set up the above class as the controller.
  • Bind the three controls to the corresponding variables.
  • Bind the actions for the Button and TextView to addName().
Some notes on the code:
  • The key step here is calling the setItems() method of the visibleList object.  This is what binds the ObservableList names to the GUI.  Once this method is called, any changes to names will automatically appear in the list.
  • Note that this call must be in the initialize() method.  The visibleList object is created after the constructor call, so we can't put this in the constructor.  JavaFX ensures that initialize() is called before any other GUI code runs.
  • By attaching addName() to both the Button and the TextView, the user can add a name either way.

Friday, January 16, 2015

Creating a minimal JavaFX user interface in Java 8

The functional programming features of Java 8 represent a very important improvement to the language. Another important improvement is the replacement of Swing by JavaFX as the primary graphical user interface framework.  I've been using Swing for about a decade, and I've now started looking at JavaFX.

There are some great tutorials on the web about the JavaFX user interface library.  But they often run too long for my attention span.  I prefer enough detail to get a minimal program working, from which I can then tinker on my own.  So in this post I will demonstrate a minimal user interface that shows the basics of how to use Scene Builder, and how to connect the interface built in Scene Builder to some Java code.

To set up Eclipse:
  • Get the all-in-one Eclipse download that has JavaFX already set up.  
  • Make sure JDK 1.8 is installed and set up as the default.  
    • Check Preferences > Java > Installed JREs to be sure.  
  • Also make sure that Preferences > Java > Compiler > JDK Compliance is also set to 1.8.
To set up Scene Builder in Eclipse:
  • Download Scene Builder 2.0
  • In Eclipse, go to Preferences > JavaFX and specify the path to the Scene Builder executable.
Once Eclipse is set up, create a new JavaFX project:
  • Go to File > New > Other > JavaFX > JavaFX Project and click "Next"
  • Call the project "minimalfx" and click "Finish"
If you check out the project on the left, it should look something like this:

The Main.java program will contain the following code.  It will run as is, producing an empty window.  The line in red is the code we will need to change in order to incorporate our own interface:

package application;
 
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;


public class Main extends Application {
 @Override
 public void start(Stage primaryStage) {
  try {
   BorderPane root = new BorderPane();
   Scene scene = new Scene(root,400,400);
   primaryStage.setScene(scene);
   primaryStage.show();
  } catch(Exception e) {
   e.printStackTrace();
  }
 }
 
 public static void main(String[] args) {
  launch(args);
 }
}

Creating an interface with interesting behavior requires two things:
  • Creating an XML file describing the interface components.
  • Modifying the above code to interact with the components in the XML file.
To this end, then, we proceed by creating the XML file as follows:
  • Go to File > New > Other > Create FXML Document.
  • Give it the name "Gui".
  • Right-click on it and select "Open with SceneBuilder"
The Scene Builder window will look something like this:


The current interface is specified in the lower left corner, under the "Document" header.  Available components are given in the upper left corner, under the "Library" header, but separated into categories.  Click on "Controls" and drag a Button into the "CENTER" of the BorderPane.  It should then look like this:


Note that the middle area now contains a representation of our interface.  Select Preview > Show Preview in Window to see what it would look like as a running program:


Now let's modify our Main.java code so that it will run this interface: 
  • Save the interface in SceneBuilder.  It is a separate program from Eclipse, so although you can run it from Eclipse its updates are not automatically reflected therein.
  • Refresh the entire project in Eclipse.  
  • Go to Main.java.  As we can see in the code above, it creates an empty BorderPane that does nothing.  We want our newly created BorderPane to be in there instead.  So replace the original BorderPane creation code (in red) with the following:
  •             
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(Main.class.getResource("Gui.fxml"));
    BorderPane root = (BorderPane) loader.load();
    
  • Run Main. It should now look like this:

Note the 400x400 size.  That's the size set in Main.java, which we can change as we see fit.

Having created the interface using SceneBuilder, and having linked it up with our code, the last thing we will do is add behavior to the Button.  Create a new Java class called Controller.  Then enter the following code:

package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Controller {
 @FXML
 private Button clickMe;
 
 private int numClicks;
 
 public Controller() {}
 
 @FXML
 private void initialize() {
  numClicks = 0;
 }
 
 @FXML
 private void clickHandler() {
  numClicks += 1;
  clickMe.setText(numClicks % 2 == 0 ? "Even" : "Odd");
 }
}

Some notes about the code:
  • The @FXML annotation allows these otherwise private elements to be accessed by SceneBuilder.  These items become options we can select within SceneBuilder for our interface.
  • The initialize() method is called when the interface is created.  Typical constructor tasks go there.
  • The JavaFX components often have the same names as the original Java Abstract Windowing Toolkit.  It's important to get the imports right to make sure we have the right components.
Now we're ready to connect the interface to its handler, the Controller class:
  • Back in SceneBuilder, select "Controller" under the Document menu.
  • In the Controller Class box, enter "application.Controller".
  • Now click on the Button we added earlier.  On the right, select the "Code" item.
  • Look for the fx:id field under the Identity header.  This will be near the top of the Code region.
  • There will be a drop-down menu with a single option: clickMe.  
    • Select it.  
    • This binds the object in SceneBuilder to the named object in the Controller class.
  • Under the Main header, go to the "On Action" field.  In the drop-down menu, select clickHandler.  
  • Save the interface in SceneBuilder and refresh the project in Eclipse.
  • SceneBuilder should look like the following at this point:

From within Eclipse, run Main once again.  Once clicking begins, the button should now respond as programmed.

And that's all there is to it.  Happy tinkering!

Monday, January 12, 2015

Java 8: A functional programming language (?)

I'm preparing to teach some Java courses this semester.  I hadn't had a chance to look at Java 8 until this past week, and so far I am impressed.  This post is not really a tutorial; it's more of a small demonstration of the possibilities.

The key to Java 8's functional features is the new stream library.  A Stream in Java is a lazy sequence of values.  Each of the collection classes in the Collections framework has a .stream() method to get a stream view of its contents.  The Arrays class also contains a static method to get a stream view of an array.  The following example, which converts each command-line argument to an integer and prints the sum of the positive values only, demonstrates the framework nicely:

 import java.util.Arrays;  
 public class SimpleDemo1 {  
      public static void main(String[] args) {  
           System.out.println              
              (Arrays.stream(args).map(s -> Integer.parseInt(s))  
                                  .filter(x -> x > 0)  
                                  .reduce(0, (x, y) -> x + y));  
      }  
 }  

Here is an equivalent program in Haskell:

 module Main where  
 import System.Environment  
 main = do args <- getArgs  
           putStrLn $ show 
                    $ foldr (+) 0 
                    $ filter (\x -> x > 0) 
                    (map read args :: [Integer])

Here are some noteworthy similarities:
  • Both languages use -> to separate anonymous function arguments from the code. 
  • Java now includes (limited) type inference and an implicit return.
  • Both use lazy evaluation of the streams.
  • Both implementations have a similar amount of text:
    • Haskell: 30 words, 223 characters
    • Java: 31 words, 264 characters
And some noteworthy differences:
  • The fundamental paradigms of each language dictate the order in which the computations are written.  Note how the Haskell example has the higher-order functions placed in the reverse order of the Java example.
  • Lazy evaluation remains pervasive in Haskell, while being confined to this new little corner of Java.
  • The syntactic overhead of converting to streams in Java is not too bad, but it is not trivial either.  Still, for Java especially, this is an impressive reduction of boilerplate.
I will definitely be including this material in my Data Structures course this semester.  I plan to post periodically regarding how I approach the topic and how the students respond.