Creating JavaFX objects in Java programs

So far, all of the examples in my previous article about how to use JavaFX objects in Java code expected the object as an input parameter. But what if you want to create a JavaFX object directly? In this article, I will describe a very simple but effective solution.

(Edit: As Mike Azzi pointed out to me (thanks again!), I should have mention, that it is also possible to create JavaFX objects by using the compiled Java classes directly. I plan to cover this later, in a series of articles about what happens during compilation.)

Let’s assume we want to create an instance of MyJavaFXClass as it was defined in the previous article. Code sample 1 shows the implementation of the class.

 1 import java.lang.System;
 2 
 3 public class MyJavaFXClass {
 4 
 5     public attribute property: String;
 6 
 7     public function printProperty() {
 8         System.out.println (property);
 9     }
10 }

Code Sample 1: Definition of MyJavaFXClass

To create an instance of this class in JavaFX Script, we would usually define an object literal. We can do that as well in Java and pass the object literal to the JavaFX-script engine we already encountered in the previous article. The script engine can parse the object literal and return a new instance of the class.

Code sample 2 shows a Java class, which creates an instance of MyJavaFXClass and uses the script engine to call its function printProperty.

 1 import javax.script.ScriptEngineManager;
 2 import com.sun.javafx.api.JavaFXScriptEngine;
 3 
 4 public class Main {
 5 
 6     public static void main (String[] args) {
 7         ScriptEngineManager manager = new ScriptEngineManager();
 8         JavaFXScriptEngine fxEngine = 
 9                 (JavaFXScriptEngine) manager.getEngineByName ("javafx");
10 
11         try {
12             Object o = fxEngine.eval
13                     ("MyJavaFXClass { property: \"JavaFX class created in Java\" }");
14             fxEngine.invokeMethod (o, "printProperty");
15         } catch (Exception ex) {
16             ex.printStackTrace();
17         }       
18     }
19 }

Code Sample 2: Constructing MyJavaFXClass in Java program

Using JavaFX sequences in a Java program

The last two articles were about how to pass and deal with primitive datatypes, Java objects, and JavaFX objects. This article focuses on JavaFX sequences.

There are quite a number of Java classes implemented to represent JavaFX sequences. Which implementation is used depends on the input parameters during creation. All of the implementations share the interface Sequence, which we must therefore use when dealing with sequences in Java code.

Using a specific sequence

The interface Sequence is generic. When using it as a parameter in a method, we can define the exact type in the method signature or we leave the type open by defining a generic method. The first code sample shows a method which expects a sequence of Integer-objects. Note that the type is not exactly defined, but used as an upper bound. This needs to be done always.

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MyIntegerLibrary {
 5 
 6     public static void printIntegerSum (Sequence<? extends Integer> seq) {
 7         int sum = 0;
 8         for (Iterator<? extends Integer> it = seq.iterator(); it.hasNext();) {
 9             sum += it.next();
10         }
11         System.out.println(sum);
12     }
13 }

Code Sample 1: Calculating sum of an Integer sequence

The method printIntegerSum in Code Sample 1 iterates through the sequence of Integers and calculates the sum. The JavaFX statement in Code Sample 2 creates a sequence of 5 integers and calls the method printIntegerSum.

 1 MyIntegerLibrary.printIntegerSum( [2, 3, 5, 7, 11] );

Code Sample 2: Calling printIntegerSum

Note that the type of this example is pretty strict. It is for example not possible to pass a sequence of Byte-objects. If you want to allow a more general sequence, you have to use a more general type. The sequence cannot be cast automatically. In the example above, the class Number can be used to allow any kind numbers in the given sequence.

Using a generic sequence

Using the interface Sequence in generic methods works as one would expect (with one exception, which will be covered later). Code Sample 3 defines a method which takes an arbitrary sequence and prints its elements using the method toString().

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MySequencePrinter {
 5 
 6     public static <T> void printSequence(Sequence<T> seq) {
 7         for (Iterator<T> it = seq.iterator(); it.hasNext();) {
 8             System.out.println(it.next().toString());
 9         }
10     } 
11 }

Code Sample 3: Printing the elements of an arbitrary sequence

The following statement calls the method with an Integer-Sequence.

 1 MySequencePrinter.printSequence( [2, 3, 5, 7, 11] );

Code Sample 4: Calling the Sequence-Printer

When defining a method which expects a sequence and a single element of the same type, you have to define the type of the sequence bounded as in the first example. Code Sample 5 defines a generic method, which takes an arbitrary sequence and an element. As you can see, the type of the sequence-elements extend the type of the single element.

 1 import com.sun.javafx.runtime.sequence.Sequence;
 2 import java.util.Iterator;
 3 
 4 public class MyElementCounter {
 5     
 6     public static <T> void countElement(Sequence<? extends T> seq, T key) {
 7         int sum = 0;
 8         for (Iterator<? extends T>it = seq.iterator(); it.hasNext();) {
 9             if (key.equals(it.next()))
10                 ++sum;
11         }
12         System.out.println(sum);
13     }
14 }

Code Sample 5: Counting element in a sequence

The method countElement in Code Sample 5 counts how often the element appears in the sequence. The following statement calls the method.

 1 MyElementCounter.countElement( [2, 3, 2, 2, 5, 7, 11, 2], 2 );

Code Sample 6: Calling counting element

If you want to know more about generic types and generric methods, this chapter of the Java Tutorial is a good start.

This article finishes the small series about how to deal with parameters passed from JavaFX to Java after primitive datatypes and Java objects were covered in the first article and JavaFX objects in the second article. The next article will be a short intermezzo on how to create JavaFX objects in Java code. After that will follow another small series which focuses some more on sequences.

Using JavaFX objects in Java code

As shown in the last article, passing parameters between a JavaFX script and Java code is fairly simple, if the parameters are native to the Java language. But if we want to pass a JavaFX-object, things are different. The main obstacle is the fact, that the Java compiler is not able to read JavaFX scripts directly to extract the needed information. But JavaFX objects can be shared between a JavaFX and Java code by using one of these approaches:

  1. Java interfaces
  2. JSR 223 – Scripting for the Java Platform
  3. com.sun.tools.javafx.ui.FXBean
  4. Using the Java classes created by the JavaFX compiler directly
  5. (JavaFX Reflection)

The last two approaches are mentioned just for completeness. It requires some detailed knowledge of how a JavaFX class is translated into Java code to use the classes directly and it is more cumbersome than the other approaches. For these reasons I’ll skip it in this article, but plan to dedicate an entire article on an overview of how JavaFX classes get translated to Java code. JavaFX Reflection is not implemented yet, but planned. I will add a description for this approach as soon as the details are decided.

Code sample 1 shows a class, which needs to be passed to Java. It has a public function printProperty, which we want to call from Java code.

import java.lang.System;

public class MyJavaFXClass {
    public attribute property: String;
    public function printProperty() {
        System.out.println(property);
    }
}

Code Sample 1: Definition of MyJavaFXClass

Java interfaces

The first approach (and my favorite one) is to use Java interfaces for passing JavaFX classes to Java code. This approach requires to define a Java interface, let the JavaFX-class implement the interface and reference the interface in the Java code. For our little example, we define the interface Printable which is shown in Code Sample 2.

public interface Printable {
    void printProperty();
}

Code Sample 2: The interface Printable

Now we can write a Java class, which takes an implementation of the interface Printable and calls its method printProperty (code sample 3).

public class MyLibraryByInterface {
    public static void print (Printable p) {
        p.printProperty();
    }
}

Code Sample 3: Using an interface to access JavaFX code

The last step is to modify the JavaFX class from code sample 1, so it implements the interface Printable. Also I added some code to create an instance of this class and pass it to the Java library.

import java.lang.System;

public class MyJavaFXClass extends Printable {
    public attribute property: String;
    public function printProperty() {
        System.out.println(property);
    }
}

var o = MyJavaFXClass {
    property: "Hello JavaFX by Interface"
};

MyLibraryByInterface.print(o);

Code Sample 4: Implementing a Java interface with JavaFX Script

This approach has some major advantages. As you can see, the Java code contains no JavaFX-specific code at all. The very same code could handle a Java-object which implements the interface Printable. As a side effect, you can easily write Java-code which can be used with JavaFX-code and Java-code at the same time. Or if you want to migrate parts of an existing Java-application, you can simply switch to use interfaces and then exchange class after class. (Well, it’s probably not going to be THAT simple, but you get the idea.) 🙂
The drawback of this approach is, that one cannot instantiate objects directly as usual when using interfaces. You either need to pass the objects to the Java code or use factory-methods. But the very first factory still needs to be passed to the Java code – or created with the next approach.

JSR 223 – Scripting for the Java Platform

JSR 223 – Scripting for the Java Platform defines mechanisms to access scripting languages from Java code. An implementation for JavaFX Script is available and part of the JavaFX Runtime. With the provided scripting engine, you can read JavaFX-files, compile, and run them. And because the engine for JavaFX Script implements the interface Invocable, it is even possible to call members of JavaFX objects.

In code sample 5, a class is defined, which takes an arbitrary object and tries to call its method printProperty with the help of the JavaFX Scripting Engine. The first two lines of the method get the ScriptingEngineManager, which manages the available scripting engines. The engine for JavaFX Script is requested and casted to the interface Invocable. The line in the try-block invokes the function printproperty of the object that was given as parameter.

import javax.script.*;

public class MyLibraryByScriptEngine {
    public static void print (Object o) {
        ScriptEngineManager manager = new ScriptEngineManager();
        Invocable fxEngine = (Invocable) manager.getEngineByName("javafx");
        try {
            fxEngine.invokeMethod(o, "printProperty");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Code sample 5: Using the JavaFX Script engine

For this approach we do not need to change the JavaFX class at all. The following two lines will create the object and pass it to the Java library.

import MyJavaFXClass;

var o = MyJavaFXClass {
    property: "Hello JavaFX by ScriptEngine"
};

MyLibraryByScriptEngine.print(o);

Code Sample 6: Calling MyLibraryByScriptEngine

As you can see, the Java code to handle a JavaFX object is slighty more complicated than in the first approach. The major drawback is, that the compiler is not able to check the code. Instead you will receive a runtime exception if for example you have a typing error in the method-name printProperty, like I did. 😉

But on the other hand this approach is the most powerful one of these three aproaches, as you are able to access JavaFX-files directly. We have just scratched the possibilities here; if you want to read more, I recommend this general introduction or this article specific to JavaFX Script.

com.sun.tools.javafx.ui.FXBean

The last approach, to use the class FXBean within the JavaFX Runtime, differs from the other approaches, because it does not give you access to the functions of a JavaFX object but to its attributes. The code sample 7 receives an arbitrary object, reads its attribute property with the help of the class FXBean, and prints the attribute.

First an FXBean is constructed using the class of the given object as parameter. The next line requests an attribute with the name property and the type string. Actually the FXBean does not return a String immeadiately, but an ObjectVariable<String>. This is the internal representation of a String-attribute mapped to Java code. We get the value of the attribute by calling the method get, which is printed in the last line of the try-block.

import com.sun.tools.javafx.ui.FXBean;
import com.sun.javafx.runtime.location.ObjectVariable;

public class MyLibraryByFXBean {
    public static void print (Object o) {
        try {
            FXBean bean = new FXBean(o.getClass());
            ObjectVariable<String> attribute =
                    (ObjectVariable<String>)bean.getObject(o, "property");
            String property = attribute.get();
            System.out.println(property);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Code Sample 7: Using the class FXBean

The next two lines of JavaFX code create an object and pass it to the Java library.

import MyJavaFXClass;

var o = MyJavaFXClass {
    property: "Hello JavaFX by FXBean"
};

MyLibraryByFXBean.print(o);

Code Sample 8: Calling MyLibraryByFXBean

As already mentioned above does this approach differ from the others and can therefore not be compared. Using the FXBean you are able to read and modify attributes directly. There are a number of methods to simplify acces to attributes of type Number, Integer, Boolean, and sequences.

After considering the primitive datatypes and Java objects in the last article, we took a look at JavaFX objects in this article. The next article will focus on dealing with sequences in Java code.

Writing Java code for JavaFX Script

When writing Java classes which can be used in JavaFX scripts, three factors have to be considered: how to create a Java object, how to access its members and how to pass parameters between Java and JavaFX.

Creating Java Objects

Creating Java objects from a JavaFX script is fairly simple, as this functionality is already built into the language. To setup a class, which can be instantiated in a JavaFX script, you just have to provide constructors as you would for the usage in Java classes. In Code Sample 1, a class with two constructors is created.

public class MyJavaClass {

    private String property;

    public MyJavaClass() {
        this.property = "Hello World";
    };

    public MyJavaClass(String property) {
         this.property = property;
    };

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

Code Sample 1: Definition of MyJavaClass

To create an instance of MyJavaClass in a JavaFX script, simply call its constructor. Notify that the brackets can be omitted, if a constructor without parameters is called.

var o1 = new MyJavaClass;
var o2 = new MyJavaClass("Goodbye World!");

Code Sample 2: Instantiating MyJavaClass in JavaFX Script

Accessing Members

Accessing members of Java objects from a JavaFX script works as it would in a pure Java environment. The access modifiers public, protected and private imply the same restrictions. As you will see in the example about passing parameters, you can also access static members defined in your Java classes. The following lines create an instance of MyJavaClass and call its method printProperty.

var o1 = new MyJavaClass("Goodbye World!");
o1.printProperty();

Code Sample 3: Accessing a member of MyJavaClass in JavaFX Script

Passing Parameters

When it comes to passing parameters, there are four categories of data types to consider. They are listed in the following table.

JavaFX Type Java Type
Number double / java.lang.Double
Integer int / java.lang.Integer
Boolean boolean / java.lang.Boolean
String java.lang.String
Java objects
JavaFX objects
Sequences

Passing primitive data types and Java objects is straightforward. The primitive data types Number, Integer, Boolean, and String are mapped to primitive data types or the equivalent objects as shown in the table. In Code Sample 4, a Java class is defined with methods to take these data types and return them.

public class MyLibrary {

    public static double returnDoublePrimitive (double d) {
        return d;
    }
    public static Double returnDoubleObject (Double d) {
        return d; 
    }

    public static int returnIntegerPrimitive (int i)     {
        return i;
    }
    public static Integer returnIntegerObject  (Integer i) {
        return i;
    }

    public static boolean returnBooleanPrimitive (boolean b) {
        return b;
    }
    public static Boolean returnBooleanObject (Boolean b) {
        return b;
    }

    public static String returnString (String s) {
        return s;
    }
}

Code Sample 4: Definition of MyLibrary

The JavaFX script in Code Sample 5 calls the Java-methods. As the code sample shows, it makes no difference to the JavaFX script, if a primitive data type or the corresponding object is needed as a parameter.

var n1: Number = 3.1415;
var n2: Number;
n2 = MyLibrary.returnDoublePrimitive(n1);
n2 = MyLibrary.returnDoubleObject(n1);

var i1: Integer = 42;
var i2: Integer;
i2 = MyLibrary.returnIntegerPrimitive(i1);
i2 = MyLibrary.returnIntegerObject(i1);

var b1: Boolean = true;
var b2: Boolean;
b2 = MyLibrary.returnBooleanPrimitive(b1);
b2 = MyLibrary.returnBooleanObject(b1);

var s1: String = "Passing a String";
var s2: String;
s2 = MyLibrary.returnString(s1);

Code Sample 5: Passing primitive JavaFX data types

When defining a new Java interface, I’d recommended to use only the data types, which are defined in JavaFX. If that is not possible, the data has to be cast. In most cases, this is done implicitly, but in some cases it has to be done explicitly using the methods byteValue(), shortValue(), longValue(), and floatValue(). To pass a char, the method charAt() of the data type String can be used. In Code Sample 6, a Java class is defined which expects these data types.

There also exist two methods doubleValue() and intValue(), which can be used to cast a Number to an Integer and vice versa. These methods can also be used with literals directly, e.g. “42.floatValue()” casts the magic number to a float.

public class MyLibraryCast {

    public static byte returnBytePrimitive (byte b) {
        return b;
    }
    public static Byte returnByteObject    (Byte b) {
        return b;
    }

    public static short returnShortPrimitive (short s) {
        return s;
    }
    public static Short returnShortObject    (Short s) {
        return s;
    }

    public static long returnLongPrimitive (long l) {
        return l;
    }
    public static Long returnLongObject    (Long l) {
        return l;
    }

    public static float returnFloatPrimitive (float f) {
        return f;
    }
    public static Float returnFloatObject    (Float f) {
        return f;
    }

    public static char returnCharacterPrimitive   (char c) {
        return c;
    }
    public static Character returnCharacterObject (Character c) {
        return c;
    }
}

Code Sample 6: Definition of MyLibraryCast

Code sample 7 shows how these methods can be called. In these cases there is a difference between using the primitive data types in Java and using the equivalent objects as you can see in the method-calls below.

var i1: Integer = 42;
var i2: Integer;

i2 = MyLibraryCast.returnBytePrimitive(i1);
i2 = MyLibraryCast.returnByteObject(i1.byteValue());

i2 = MyLibraryCast.returnShortPrimitive(i1);
i2 = MyLibraryCast.returnShortObject(i1.shortValue());

i2 = MyLibraryCast.returnLongPrimitive(i1) as Integer;
i2 = MyLibraryCast.returnLongObject(i1.longValue()).intValue();

var n1: Number = 3.1415;
var n2: Number;

n2 = MyLibraryCast.returnFloatPrimitive(n1.floatValue());
n2 = MyLibraryCast.returnFloatObject(n1.floatValue());

var s1: String = "c";
var s2: String;

s2 = java.lang.Character.toString (MyLibraryCast.returnCharacterPrimitive (s1.charAt(0)) );
s2 = MyLibraryCast.returnCharacterObject(s1.charAt(0)).toString();

Code Sample 7: Passing primitive Java data types by casting

A Java object does not need to be mapped, but is passed as the very same object. In the Java code it makes no difference if the Java object was passed from a JavaFX script or from some other Java code. The static method in Code Sample 8 take an instance of MyJavaClass (see Code Sample 1), calls its method printProperty and returns the object.

public class MyLibraryForObjects {
    public static MyJavaClass printAndReturnMyJavaClass (MyJavaClass o) {
        o.printProperty();
        return o;
    }
}

Code Sample 8: Definition of MyLibraryForObjects

The JavaFX script in Code Sample 9 creates an instance of MyJavaClass, passes it to the method printAndReturnMyJavaClass and saves the result.

var o1: MyJavaClass = new MyJavaClass("Hello Java!");
var o2: MyJavaClass;

o2 = MyLibraryForObjects.printAndReturnMyJavaClass(o1);

Code Sample 9: Passing a Java object

Passing JavaFX objects and sequences to Java is slightly more difficult and will be covered in the next articles.