Cuando Sun presentó JavaFX en JavaOne 2007, JFX Script era aún un lenguaje interpretado. Sin embargo, desde un primer momento se aclaró que esto era sólo temporal, mientras el lenguaje pasaba de ser el resultado de la investigación personal (F3, de Chris Oliver) a un producto a ser introducido en el mercado. Es así como, después de un lapso de tiempo, JFX Script pasó de ser un lenguaje interpretado, a un lenguaje compilado, principalmente porque uno de las características principales que Sun pretende para las aplicaciones escritas en este lenguaje es un nivel de performance mayor que aquellas escritas en Javascript o ActionScript (de hecho, en los benchmarks realizados por Chris Oliver, el rendimiento de JFX Script fue 12 veces mayor que el de ActionScript.
Al cambio de categoría del lenguaje se sumaron algunos cambios en la sintaxis, los cuales revisaremos hoy, siguiendo la guia de migración de Planet JFX (simplemente porque fue una de las más completas que conseguí), como para dar por terminada esta parte, y comenzar luego con ejemplos algo más complejos y aggiornados.
Operaciones
Antes: el lenguaje diferenciaba entre funciones y operaciones.
class Foo {
function times2(x) { return x * 2; }
operation print(s) { System.out.println(s); }
}
|
Ahora: La sintaxis actual fusiona los dos conceptos en uno único: el de función, y utiliza la palabra reservada “function” para declararlas. Es decir, el único cambio que debemos realizar es el de renombrar las operaciones a funciones.
class Foo {
function times2(x) { return x * 2; }
function print(s) { System.out.println(s); }
}
|
Inicialización de atributos
Antes: los valores iniciales de los atributos debían declararse fuera del cuerpo de la clase.
class Foo {
attribute bar: Boolean;
}
attribute Foo.bar = true;
|
Ahora: esto se hace igual que en Java, es decir, dentro de la clase.
class Foo {
attribute bar: Boolean = true;
}
|
Triggers de reemplazo
Antes: los triggers de reemplazo se definían fuera de la clase, utilizando la sintaxis “trigger on atributo = valor”
Sin inicialización:
class Foo {
attribute bar: Boolean;
}
trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}
Con inicialización:
class Foo {
attribute bar: Boolean = true;
}
trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}
|
Ahora: se definen dentro de la clase, como parte de la declaración del atributo, utilizando la sintaxis “on replace”
Sin inicialización:
class Foo {
attribute bar: Boolean on replace {
if (bar == true) {
beep();
}
};
}
Con inicialización:
class Foo {
attribute bar: Boolean = true on replace {
if (bar == true) {
beep();
}
};
}
|
Cardinalidad
Antes: el operador de cardinalidad era el asterisco (’*').
class Foo {
attribute names :String*;
}
attribute names = ["Monica", "Rachel", "Phoebe"];
|
Ahora: se utilizan en lugar del asterisco, los corchetes (”[]“).
class Foo {
attribute names :String[] = ["Monica", "Rachel", "Phoebe"];
}
|
Literales objeto sin atributos
Antes: podíamos referirnos a literales objeto sin atributos utilizando solo el nombre de su clase.
Frame {
title: "Show MenuSeparator"
height: 180
width: 320
menubar: MenuBar {
menus: Menu {
text: "File"
items: [MenuItem {
text: "New"
}, MenuItem {
text: "Open"
}, MenuItem {
text: "Save"
}, MenuSeparator, MenuItem {
text: "Import"
}]
}
}
visible: true
}
|
Ahora: adicionalmente, debemos utilizar llaves (”{}”).
Frame {
title: "Show MenuSeparator"
height: 180
width: 320
menubar: MenuBar {
menus: Menu {
text: "File"
items: [MenuItem {
text: "New"
}, MenuItem {
text: "Open"
}, MenuItem {
text: "Save"
}, MenuSeparator {
}, MenuItem {
text: "Import"
}]
}
}
visible: true
}
|
Instancias con nombre
Antes: era posible utilizar algunas instancias con nombre, que representaban constantes predefinidas.
Frame {
title: "White Frame"
background: white
...
}
|
Ahora: es necesario utilizar las constantes predefinidas, o literales objeto.
Frame {
title: "White Frame"
background: Color.WHITE
...
}
O también:
Frame {
title: "White Frame"
background: Color {
red: 1
green: 1
blue: 1
opacity: 1
}
...
}
|
Literales objeto anónimo
Antes: podíamos utilizar literales anónimos y dejar que el interprete infiriese el tipo de ese bloque de código.
...
accelerator: {
modifier: CTRL
keyStroke: O
}
....
|
Ahora: ante la ausencia de interprete, es obligatorio declarar el tipo de cada bloque.
...
accelerator: Accelerator {
modifier: KeyModifier.CTRL
keyStroke: KeyStroke.O
}
...
|
Sobreescribir funciones
Antes: podíamos sobreescribir funciones sin necesidad de colocar el tipo de retorno en la declaración de las mismas.
class MyWidget extends CompositeNode {
...
function composeNode() {
...
}
}
|
Ahora: es obligatorio escribirlo.
class MyWidget extends CompositeNode {
...
function composeNode() :Node {
...
}
}
|
Binding bidireccional
Antes: bastaba con colocar la palabra reservada “bind“.
...
TextField {
value: bind model.firstName
}
...
|
Ahora: la sintaxis de JFX Script compilado requiere del uso de la clausula “with inverse“.
...
TextField {
value: bind model.firstName with inverse
}
...
|
Casting de Number a Integer
Antes: el casting era automático.
...
var real :Number;
num = 6.42;
var integer :Integer;
integer = real;
...
|
Ahora: para evitar perdida de precisión en la compilación, debe utilizarse la función “intValue“.
...
var real :Number;
num = 6.42;
var integer :Integer = real.intValue();
...
|
Herencia
Al menos hasta la versión del compilador de JavaFX de marzo de 2008, puede que sea necesario utilizar la palabra reservada “as” para evitar algunos problemas de herencia. Es decir, lo que antes hubiesemos hecho de esta forma:
class Foo extends Rectangle {
...
}
...
...
content: Canvas {
content: Foo {
...
}
...
}
...
}
|
Ahora debemos hacerlo así:
class Foo extends Rectangle {
...
}
var foo :Foo = Foo {
...
};
...
content: Canvas {
content: foo as Node
...
}
...
}
|
Bucles
Antes: existían dos bucles, “for” y “foreach“.
...
for (Integer i = 0; i < element.length; i++) {
System.out.println(element);
}
...
foreach (element in group) {
System.out.println(element);
}
...
|
Ahora: al igual que con las funciones y operaciones, estos dos conceptos se fusionaron en uno solo que utiliza la palabra reservada “for” en su declaración. Este puede ser utilizado tanto como el “for” de Java así como el “foreach” de JFX Script.
...
for (Integer i = 0; i < element.length; i++) {
System.out.println({element});
}
...
for (element in group where element.length() < 4) {
System.out.println({element});
}
...
|
Si bien esto no fue más que una traducción de la guía de conversión a JFX Script compilado, bastará para que, junto con las partes anteriores de la guía, tengamos una lista mas o menos completa de la sintaxis del lenguaje. A partir de ahora, comenzaremos crear algunas aplicaciones sencillas, para comprender el funcionamiento de las funciones gráficas básicas. Como siempre, les dejo algunos sitios recomendados para visitar mientras voy dándole forma a las próximas partes de este tutorial: