Java FX Script, primeros pasos - Parte 3
Antes que nada, pido perdón por no respetar la frecuencia de publicación que prometí, pero esta semana tuve un problema familiar y se me atrasó todo el trabajo. Sin más excusas, ahora si, la última entrega de la "referencia" de JavaFX script. En la primera y la segunda entrega en conjunto, ya conocimos:
- Las características y el propósito de este lenguaje.
- Los tipos primitivos
- Declaración de variables
- Expresiones
- Manejo de cadenas de caracteres
- Arreglos
- El operador de selección
- Queries sobre arreglos
- Formateo de cadenas de caracteres
En esta entrega, terminaremos entonces de cubrir los tópicos de la referencia oficial de JFX Script, comenzando por la declaración de clases:
En JavaFX, la declaración de clases consiste en colocar la palabra reservada "class" seguida del nombre que deseamos darle. Finalmente, se coloca la lista de atributos, operaciones y funciones entre llaves, separadas por punto y coma. Si nuestra clase hereda funcionalidad de una o más clases, se coloca luego de su nombre la palabra reservada "extends", seguida de la lista de clases "padre" separadas por comas (a diferencia de Java, en JFXS sí existe la herencia múltiple):
class Persona {
attribute nombre: String;
attribute padre: Persona inverse Person.hijos;
attribute hijos: Persona* inverse Person.padre;
function obtenerIngresosFamiliares(): Number;
function obtenerCantidadDeHijos(): Number;
operation casarseCon(pareja: Persona): Boolean;
}
|
Atributos, funciones y operaciones:
En JavaFX un atributo se declara utilizando la palabra reservada "attribute" seguida del nombre deseado y, opcionalmente, el tipo, la cardinalidad ('?', '*' o '+') y una clausula "inverse" continuación. Declarar un atributo como "inverse" de otro implica que el interprete de JFXS realizará actualizaciones (inserción, borrado o reemplazo) en el atributo inverso ante una modificación en el atributo original.
Los atributos multivaluados (es decir, los declarados con '*' o '+') son tratados como arreglos y, por ende, puede accederse a la información contenida en los mismos utilizando el operador de selección ("[]").
La contraparte en JavaFX de los métodos en Java son las funciones y operaciones. Las primeras representan un subconjunto puramente funcional del lenguaje, se declaran utilizando la palabra reservada "function" y solo pueden contener declaraciones de variables y una sentencia de retorno. Son especialmente útiles para realizar operaciones matemáticas sencillas, por ejemplo:
function z(a,b) {
var x = a + b;
var y = a - b;
return sq(x) / sq (y);
}
function sq(n) {return n * n; }
|
Las operaciones, por su parte, pueden contener cualquier cantidad de sentencias de control: condicionales, bucles, try and catch, etc Es decir, son bastante más similares a un método Java tradicional que las funciones. Se declaran utilizando la palabra clave "operation" y, además del nombre, debe colocarse la lista de parámetros entre paréntesis y el tipo de retorno deseado utilizando ": tipo de retorno", por ejemplo:
operation substring(s:String, n:Number): String {
try {
return s.substring(n);
} catch (e:StringIndexOutOfBoundsException) {
throw "indice fuera de rango";
}
}
|
A diferencia de los métodos Java, en JFX el cuerpo de las funciones y operaciones debe definirse fuera de la clase:
function Persona.obtenerCantidadDeHijos() {
return sizeof this.hijos;
}
|
Así mismo, si deseamos darle valores iniciales a los atributos, debemos hacerlo fuera de la definición de la clase:
import java.lang.System;
class X {
attribute a: Number;
attribute b: Number;
}
attribute X.a = 10;
attribute X.b = -1;
var x = new X();
System.out.println(x.a); // imprime 10
System.out.println(x.b); // imprime -1
|
Una vez definida nuestra clase, lo lógico es que creemos una o más instancias de la misma, para hacer esto JFXS soporta dos mecanismos: el primero, la sintaxis clásica de Java y, el segundo, la sintaxis declarativa propia de este lenguaje. La sintaxis clásica de Java consiste en realizar una llamada al constructor de la clase, pasando los valores de los atributos en el orden en que fueron declarados. La sintaxis declarativa consiste en: abrir llaves, para cada atributo colocar el nombre, dos puntos y el valor pretendido y finalizar cerrando llaves. Veamos un ejemplo utilizando la clase "Date" de Java:
import java.util.Date;
import java.lang.System;
var date1 = new Date(95, 4, 23); // llamada a un constructor Java
var date2 = Date { // creación de un "object literal" usando sintaxis declarativa
month: 4
date: 23
year: 95
};
|
Triggers
A diferencia de Java, las clases en JFXS no poseen constructores y, en general no se utilizan "setters" para los atributos. En vez de eso, el lenguaje provee un conjunto de triggers (similares a los de SQL) que permiten la manipulación de estos datos. En pocas palabras un trigger (que en castellano vendría siendo algo como "gatillo") es un conjunto de instrucciones que se programan para "dispararse" en respuesta a un evento dado. Es decir, la ocurrencia del evento presiona el gatillo que inicia la ejecución de las instrucciones. En JFXS pueden programarse triggers que respondan a eventos de creación de objetos, inserción, reemplazo y borrado de valores de atributos multivaluados.
Triggers de creación de objetos
los triggers de este tipo responden ante la creación de un objeto del tipo declarado en la sentencia "on new nombreDeLaClase". Por ejemplo:
import java.lang.System;
class X {
attribute nums: Number*;
}
trigger on new X {
insert [3,4] into this.nums;
}
var x = new X();
System.out.println(x.nums == [3,4]); // imprime true
|
Este trigger inserta los elementos 3 y 4 dentro del atributo multivaluado "numbs" de cada instancia de la clase X, al momento de su creación. Nótese la utilización de la palabra reservada "this" para indicar que nos referimos a la instancia actual.
Triggers de inserción
Un trigger de este tipo se disparará al momento de realizar una inserción en un atributo multivaluado. Por ejemplo:
import java.lang.System;
class X {
attribute nums: Number*;
}
trigger on insert num into X.nums {
System.out.println("acaba de insertar {num} en X.nums en la posición {indexof num}");
}
var x = new X();
insert 12 into x.nums;
// imprime "acaba de insertar 12 en X.nums en la posición 0"
insert 13 into x.nums;
// imprime "acaba de insertar 13 en X.nums en la posición 1"
|
Nótese el uso del mecanismo de inserción de inclusión de referencias a variables encerrándolas entre llaves, así como el uso de la instrucción "indexof ".
Triggers de borrado
Complementariamente a los triggers de inserción, existen los triggers que se disparan al borrarse un elemento de un atributo multivaluado:
import java.lang.System;
class X {
attribute nums: Number*;
}
trigger on delete num from X.nums {
System.out.println("acaba de borrar {num} de X.nums en la posición {indexof num}");
}
var x = X {
nums: [12, 13]
};
delete x.nums[1];
// imprime "acaba de borrar 13 de X.nums en la posición 1"
delete x.nums[0];
// imprime "acaba de borrar 13 de X.nums en la posición 0"
|
Nota: "num" es sólo un nombre elegido al azar, la referencia a la variable que será insertada (o borrada) puede tomar cualquier nombre.
Triggers de reemplazo
Estos triggers se disparan al reemplazar el valor de un atributo por uno nuevo. En el ejemplo, "valorViejo" y "valorNuevo" son nombres elegidos al azar para las variables que tomaran los valores respectivos del elemento actual. Al igual que el nombre "num" de los ejemplos anteriores, estos nombres pueden ser cambiados a voluntad.
import java.lang.System;
class X {
attribute nums: Number*;
attribute num: Number?;
}
trigger on X.nums[valorViejo] = valorNuevo {
System.out.println("X.nums: se reemplazo {valorViejo} con {valorNuevo} en la posición {indexof valorNuevo}");
}
trigger on X.num[valorViejo] = valorNuevo {
System.out.println("X.num: se reemplazo {valorViejo} con {valorNuevo}");
}
var x = X {
nums: [12, 13]
num: 100
};
x.nums[1] = 5;
// imprime "X.nums: se reemplazo 13 con 5 en la posición 1"
x.num = 3;
// imprime"X.num: se reemplazo 100 con 3"
x.num = null;
// "X.num: se reemplazo 3 con null"
|
Sentencias
JFXS soporta versiones similares de aquellas de Java. A saber:
if/ else: igual a la de Java, solo que requiere el uso obligatorio de llaves:
if (condition1) {
System.out.println("Condition 1");
} else if (condition2) {
System.out.println("Condition2");
} else {
System.out.println("not Condition 1 or Condition 2");
}
|
while: igual a la de Java, también requiere del uso de llaves obligatorio:
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
System.out.println("i = {i}");
i += 1;
}
|
try, catch y throw: iguales a las de Java, pero con la declaración de variables propia de JFXS. Es importante notar que en JFXS cualquier objeto puede ser "arrojado" y/o "atrapado", no sólo aquellos que extienden "java.lang.Throwable"
try {
throw "Hola";
} catch (s:String) {
System.out.println("atrape un String: {s}");
} catch (any) {
System.out.println("atrape algo que no es un Sting: {any}");⁞
} finally {
System.out.println("finally...");
}
|
for: en JFXS, la sentencia for utiliza la misma sintaxis que "foreach" (una de las "list comprehensions" que vimos en la segunda parte del curso):
for (i in [0..10]) {
System.out.println("i = {i}");⁞
}
// imprime solo los números pares
for (i in [0..10] where (i%2 == 0) ) {
System.out.println("i = {i}");
}
// imprime solo los números impares utilizando un rango
for (i in [1,3..10]) {
System.out.println("i = {i}");
}
// imprime el producto cartesiano
for (i in [0..10], j in [0..10]) {
System.out.println(i);
System.out.println(j);
}
|
return: identica a la de java:
operation add(x, y) {
return x + y;
}
|
break y continue: identicas a sus pares Java, solo que en JFXS no soportan el uso de etiquetas. Deben aparecer dentro de un bucle for o while:
operation foo() {
for (i in [0..10]) {
if (i > 5) {
break;
}
if (i % 2 == 0) {
continue;
}
System.out.println(i);
}
}
operation bar() {
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
if (i % 2 == 0) {
continue;
}
System.out.println(i);
i += 1;
}
}
|
do y do later: Permite ejecutar un bloque de código JFX en un hilo en segundo plano. Normalmente, todo el código JFX se ejecuta en un único hilo (en particular en el hilo AWT Event Dispatch), unicamente el código contenido dentro de una sentencia "do" puede ejecutarse en otro hilo:
import java.net.URL;
import java.lang.StringBuffer;
import java.lang.System;
import java.io.InputStreamReader;
import java.io.BufferedReader;
// En el hilo AWT Event Dispatch
var result = new StringBuffer();
do {
// En un nuevo hilo en segundo plano
var url = new URL("http://www.foo.com/abc.xml");
var is = url.openStream();
var reader = new BufferedReader(new InputStreamReader(is));
var line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
result.append(line);
result.append("\n");
}
}
// Nuevamente en el hilo AWT Event Dispatch
System.out.println("result = {result}");
|
El ejemplo anterior vemos como es posible mandar operaciones a segundo plano mientras continuamos con la ejecución de nuestra aplicación. En general esto es muy útil para no interrumpir la ejecución de las instrucciones propias de la interfaz gráfica, rompiendo así con la fluidez de la aplicación. Sin embargo, el código dentro de una sentencia "do" sólo puede acceder a objetos Java que, de ser necesario, deberán hacerse cargo de su sincronización.
La sentencia "do later", en cambio, permite una ejecución asincronica del código que envuelve dentro del hilo AWD, en vez de una ejecución sincronica fuera del mismo (como lo hace "do"):
import java.lang.System;
var saying1 = "Hola Mundo";
var saying2 = "Adios Mundo Cruel";
do later {
System.out.println(saying1);
}
System.out.println(saying2);
//imprime: Adios Mundo Cruel Hola Mundo |
Evaluación incremental
Finalmente, la cereza de la torta: el mecanismo de evaluación incremental de JFXS. Este mecanismo permite que el valor de dos atributos se vincule utilizando la instrucción "bind", creando así en una especie de "Observer - Observable" entre ellos. De esta forma, el valor de un atributo cambiará cuando el atributo vinculado a este lo haga. Utilizando este mecanismo es posible encadenar los vínculos para generar interfaces gráficas que respondan en formas complejas a distintos eventos.
Veamos un ejemplo sencillo:
import java.lang.System;
class X {
attribute a: Number;
attribute b: Number;
}
var x1 = X {
a: 1
b: 2
};
var x2 = X {
a: x1.a // no incremental
b: bind x1.b // incremental
};
System.out.println(x2.a); // imprime 1
System.out.println(x2.b); // imprime 2
x1.a = 5;
x1.b = 5;
System.out.println(x2.a); // imprime 1
System.out.println(x2.b); // imprime 5
|
Es fácil ver que el vinculo se da entre X1.b y X2.b, nótese que cuando el primero se actualiza, el segundo lo hace también. En el caso de las funciones, los valores que se modifican dentro de las mismas son evaluados incrementalmente (es decir, si estaban vinculados, los vínculos tendrán efecto). Por el contrario, dentro de las operaciones los valores no son evaluados incrementalmente.
Otra versión de la evaluación incremental es la "perezosa" (lazy). En concordancia con su nombre, esta evaluación espera que el valor sea sometido a una lectura para verificar si debe actualizarse. Es común el uso de este mecanismo para manipular estructuras recursivas de datos. Modificando el ejemplo anterior podemos ver la evaluación perezosa en acción:
import java.lang.System;
class X {
attribute a: Number;
}
var x1 = X {
a: 1
};
var x2 = X {
a: bind lazy x1.a
// aún no se asignará ningún valor
};
System.out.println(x2.a);
// al leerlo, el valor se actualiza, e imprime 1
|
Con esto completamos todos los elementos de mayor importancia de JavaFX Script en su versión interpretada. Les recomiendo, al igual que al finalizar la entrega anterior, que practiquen y se familiaricen con la utilización de los mecanismos propios de este lenguaje. Nos vemos en la próxima entrega.

Hola Ezequiel otra vez yo, no recuerdo bien el url donde lei los cambios que se produjeron sobre javaFX, pero tengo entendido que algunas cosas ya cambiarón su sintaxis. Por ej.
A diferencia de los métodos Java, en JFX el cuerpo de las funciones y operaciones debe definirse fuera de la clase.
Esto ahora si esta permitido según lo que leí.
Así mismo, si deseamos darle valores iniciales a los atributos, debemos hacerlo fuera de la definición de la clase.
Esto ahora también esta permitido y te pego los ejemplos de muestra
Antés:
class Foo {
attribute bar: Boolean; }
attribute Foo.bar = true;
Ahora:
class Foo { attribute bar: Boolean = true; }
Y si no me equivoco el trigger de reemplazo se realiza ahora por medio de la función replace.
Antés:
class Foo {
attribute bar: Boolean;
}
trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}
Ahora:
class Foo {
attribute bar: Boolean on replace {
if (bar == true) { beep();
}
};
}
Antés con Inicialización:
class Foo {
attribute bar: Boolean = true;
}
trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}
Ahora con Inicialización:
class Foo {
attribute bar: Boolean = true on replace {
if (bar == true) {
beep();
}
};
}
Ezequiel por favor, vos podrías verificar si lo que te escribi es así, ya que creo que puedes tener más info de esto. Desde ya muchas gracias.
Enviado por Emilio en agosto 17, 2008 a las 01:36 AM GMT-03:00 #
Emilio, estas tres primeras entregas cubrieron la referencia de la primera versión de JavaFX Script (la interpretada). En la próxima veremos todos los cambios que se dieron al pasar a JFXS compilado.
Adelantándome un poco, te cuento que todo lo que decís en los comentarios (tanto en los de la segunda como los de la tercera entrega) es cierto y, como te decía antes, lo veremos en mayor detalle en la próxima entrega, que va a estar dedicada específicamente a los cambios entre las versiones.
Enviado por Ezequiel Aranda en agosto 18, 2008 a las 10:16 AM GMT-03:00 #
Emilio:
Ezequiel gracias por tu repuesta, me aclaraste mis dudas, con la misma. Otra vez te agradezco por el material posteado y repuestas. Quedo ansioso en la espera de tu próximo material de JavaFX.
Enviado por Emilio en agosto 18, 2008 a las 10:56 AM GMT-03:00 #
Bueno nuevamente estoy por el tercer capitulo y ahora se esta poniendo mas interesante hay partes que no entiendo pero ya estoy preparando preguntas para ustedes los maestros nuevamente felicitarlos y ansioso por el 4 capitulo que lo vere en estos días un abrazo y saludos a todos
Enviado por yamilfg en febrero 22, 2009 a las 03:40 PM GMT-03:00 #