Using Java Objects Within JavaFX Programs

   
By Michael Heinrichs, May 2008  

The seamless integration of JavaFX Script with pure Java code is one of the most interesting features of JavaFX Script. Using Java objects within JavaFX Script is simple, because JavaFX Script was designed with this feature in mind and the necessary instruments have been build into the language. This article will present possible ways to create JavaFX objects and use them in Java code.

Contents
 
 
Who Creates Whom?
Creating JavaFX Objects in Java
Working with JavaFX objects
Implementing Java Interfaces
(Reflection)
Using JavaFX sequences in Java
Creating sequences
Working with sequences
References
About the Author
 
Who Creates Whom?

When designing a system that includes components written in JavaFX Script as well as components done in pure Java language, one of the first questions that comes up is which technology will be responsible for the startup. There is no correct answer, as it depends on the context. The most common scenario is that the main application was written in JavaFX Script and uses Java libraries for the technical tasks. But there are also scenarios for the opposite approach, where code written in the Java language creates objects implemented in JavaFX Script. In a Java application, for example, the GUI layer may be migrated to JavaFX Script, while the businesslogic (including the startup procedure) is as unchanged as possible.

In the first scenario, a component written in JavaFX Script that creates Java objects can be achieved by calling the constructor of the Java classes in JavaFX Script in much the same way as one would in a pure Java environment. The second scenario needs slightly more consideration. One approach could be to create JavaFX objects directly by using the constructors generated by the compiler. But this approach is discouraged, because it is generally not desirable to depend on implementation details of the compiler.

Creating JavaFX Objects in Java

There are ways to create JavaFX objects from Java code without calling the constructor directly. The simplest solution is to use a factory, which is written in JavaFX Script. This solution has the advantage that the objects are created in JavaFX Script and therefore the complete range of possibilities allowed in object literals are available during the process of creation. How the methods of a factory written in JavaFX can be called from a Java application is described in the section below.

Although this approach is simple and yet very powerful, there is a major drawback. For the very first object (such as the factory itself), a different solution needs to be implemented. The most flexible approach to create JavaFX objects is to use the Script engine that is part of the compiler. It allows you to parse and execute JavaFX Script on-the-fly. A script can be passed as a text file or as a string argument. The following example will pass a string argument to create a JavaFX object: It shows a simple JavaFX class with one attribute property and a single method, printProperty().

Code Sample 1: Definition of MyJavaFXClass
import java.lang.System;

public class MyJavaFXClass {

    public attribute property: String;

    public function printProperty() {
        System.out.println(property);
    }
}
 

The Java class in Code Sample 2 creates an instance of MyJavaClass, initializing the attribute, and calls the method printProperty(). To run this example, the library javafxc.jar, which is not a mandatory library for the JavaFX runtime, needs to be in the classpath.

The first step is to create an instance of a ScriptEngineManager. The ScriptEngineManager contains all script engines supported by the runtime. The ScriptEngineManager is needed to get the JavaFXScriptEngine. Script engines are typically identified by their name or the file-extensions of associated scripts. In this case, the engine is identified by its name, "javafx".

The next two statements define the JavaFX Script to be executed. The script consists of an object literal, which creates an instance of MyJavaFXClass and sets the attribute. The script is executed by calling the method eval() of JavaFXScriptEngine and returns the new object. The next line executes the method printProperty() of the returned object.

Code Sample 2: Constructing MyJavaFXClass in Java program
import javax.script.ScriptEngineManager;
import com.sun.javafx.api.JavaFXScriptEngine;

public class Main {

  public static void main (String[] args) {
    ScriptEngineManager manager =
        new ScriptEngineManager();

    JavaFXScriptEngine fxEngine = 
        (JavaFXScriptEngine) manager.getEngineByName("javafx");

    try {

      String param = "JavaFX object created in Java";

      String script = String.format (
          "MyJavaFXClass {property: \"%s\"}", param);

      Object o = fxEngine.eval(script);

      fxEngine.invokeMethod(o, "printProperty");

    } catch (Exception ex) {
      ex.printStackTrace();
    }           
  }
}
 
Working with JavaFX objects

Once the Java objects and the JavaFX objects are created one way or the other, communication between both worlds has to be implemented. Again there is the simple scenario (JavaFX Script calling Java methods) and a more challenging one. The Java compiler is not able to parse JavaFX Script and is therefore not able to extract the needed information from the sources.

One might again be tempted to access the compiled Java classes directly. Although this is currently possible, it is not recommended for the same reason that the generated constructor should not be used directly. It would introduce a number of dependencies on the implementation details of the compiler. There is no guarantee that the compilation of JavaFX won't be changed in the future, therefore relying on the byte code that is currently created introduces some heavy risks in terms of compatibility.

Another solution to access a method of a JavaFX object was already presented in the example above. The JavaFXScriptEngine was used to call the method printProperty() of the returned object. Although this is probably the most flexible approach, it is cumbersome to use on a larger scale. It is also error-prone, because most errors show up as runtime-errors. A good alternative is to use Java interfaces to simplify communication between JavaFX Script and the Java language.

Implementing Java Interfaces

JavaFX classes are able to implement Java interfaces just as well as Java classes. This feature can be used to allow Java components to access JavaFX objects as they would access any other implementation of an interface. In fact the Java components do not even need to know that they are dealing with JavaFX objects under the hood.

As an example, the method printProperty() from MyJavaFXClass in Code Sample 1 should be called from a Java program. First an interface needs to be set up to define the members, which are going to be called from Java code. Code Sample 3 shows the interface Printable. It defines one accessible member, the method printProperty().

Code Sample 3: The interface Printable
public interface Printable {
    void printProperty();
}
 

Next step is to make sure the class MyJavaFXClass implements the interface Printable. This can be achieved by putting Printable in the extends-list of MyJavaFXClass as shown in Code Sample 4.

Code Sample 4: MyJavaFXClass implementing a Java interface
public class MyJavaFXClass extends Printable
 

With this change MyJavaFXClass is ready to be used in Java programs. Code Sample 5 shows a small "library" consisting of one static class to print a Printable. If an instance of MyJavaFXClass is passed to print(), the method printProperty() of the given Printable is called. There is no code specific to JavaFX Script in the library. A Java class, which implements Printable, can as well be consumed by the method print() as a JavaFX class.

Code Sample 5: Working with JavaFX objects via interface
public class MyPrinterLibrary {
    public static void print (Printable p) {
        p.printProperty();
    }
}
 
(Reflection)

A fourth alternative is the Reflection API of the JavaFX runtime. It will be accessible from JavaFX Script and from Java programs, and is currently under development. In the near future, it may provide another way to access JavaFX objects.

Using JavaFX sequences in Java

Sequences are a fundamental data type in JavaFX. But they do not exist in the Java language and therefore were implemented as a new data structure. Currently there exist more than a dozen Java classes to implement JavaFX sequences. Which implementation(s) are used depends on the way the sequence was assembled. But all of the implementations share the same interface com.sun.javafx.runtime.sequence.Sequence, which is part of the JavaFX-runtime javafxrt.jar, and which needs to be used when dealing with sequences in Java programs.

Creating sequences

The helper class com.sun.javafx.runtime.sequence.Sequences, which is part of the JavaFX-runtime javafxrt.jar, contains several methods to create JavaFX sequences. First of all, there is a group of fromArray()-methods to create sequences directly from arrays. For arrays of primitive datatypes, the elements' type of the new sequence is determined automatically. Table 1 shows the relationship. It is also possible to set the elements' type of the returned sequence explicitly. This works for all kinds of arrays.

Table 1: Relationship between Java arrays and created JavaFX sequences
Java array
JavaFX sequence
int[], short[], byte[], char[]
Integer[]
double[], float[]
Number[]
long[]
java.lang.Long[]
boolean[]
Boolean[]
 

The factory method make() in the helper class Sequences comes in three flavors. It allows you to create a sequence from a java.util.List, from a part of an array or by listing the elements explicitly. There is also a third method emptySequence(), which is very useful. It creates an empty sequence for a given element type.

Working with sequences

The interface Sequence is generic; the type variable specifies the sequence's elements. Code Sample 6 shows a Java class that creates a sequence of Integer and iterates through all elements to calculate the sum. The type variable is not exactly defined, but set as an upper bound. This is an implementation obscurity and always should be done.

The interface Sequence contains a number of useful methods to interact with the sequence. The method iterator() returns an Iterator for the elements of the sequence. Because the sequence is specified to contain Integers, the type variable of the Iterator is defined as well and therefore it.next() can be added to sum without any class casting.

import com.sun.javafx.runtime.sequence.Sequence;
import com.sun.javafx.runtime.sequence.Sequences;
import java.util.Iterator;
 
public class IntegerSequenceSum {

  public static void main (String[] args) {
    Sequence<? extends Integer> seq = 
        Sequences.make(Integer.class, 2, 3, 5, 7, 11);

    int sum = 0;
    for (Iterator<? extends Integer> it = seq.iterator();      
        it.hasNext();) {
      sum += it.next();
    }

    System.out.println(sum);
  }
}
 
References
 
About the Author

Michael Heinrichs is a software engineer on Sun's Java ME development team.

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.