GWT 2.8.0 è stato (quasi) rilasciato

Mentre scrivo queste righe è già da un mesetto che è stata rilasciata la Beta1
di GWT 2.8.0 che di fatto diventerà la RC1. Infatti per il momento l'unica differenza tra la Beta e la RC è che la Classic Dev Mode sarà ufficialmente
deprecata e quindi non più supportata.

EDIT: prima di continuare a leggere è bene che tu sappia che sul sito ufficiale del progetto GWT sulle note di rilascio della 2.8.0 beta è stata riportata la specifica finale della JsInterop, per la quale appena possibile scriverò la seconda parte di questo post

EDIT: [aggiornamento al 29 Luglio 2016] sul repository ufficiale sono presenti le note di rilascio della RC1 di GWT 2.8 la novità più interessante è l'emulazione Java 8 delle nuove API dell'SDK (interfacce funzionali, stream, etc), correzione di bug importanti durante la fase di compilazione, e compilazione JS con le nuove keyword di ES6

Una piccola nota dolente di questo rilascio è la scarsa nota di degno che sta avendo sul sito gwtproject.org, mi stupisce come non sia stata ancora enfatizzata la Js Interoperability che di fatto rivoluzionerà l'approccio di GWT verso Javascript. C'é anche da dire che lo stato dell'arte è una beta quindi tutto può cambiare. Ultimamente GWT è stato un po' offuscato dall'imminente ES6. La verità è che GWT continuerà ad esistere e, per la gioia degli sviluppatori Java, permetterà di creare moduli scritti in Java completamente integrabili con Javscript. Infatti tra le novità più importanti di questa tanto attesa release troviamo:

  • il supporto a Java8
  • JsInterop
  • Ottimizzazioni di compilazione delle SDM (Super Dev Mode)
  • Compilazione ES6

Tralasciando la parte sulle doverose ottimizzazioni della SDM mi soffermerei di più sulle prime due. Secondo me non è un caso che siano state rilasciate insieme, Java8 infatti offre delle API e dei costrutti per il supporto alla programmazione funzionale mentre la JsInterop permette di comporre i tipi che vogliamo esportare in Javascript tramite delle annotation pensate per istruire il compilatore a generare il codice che vogliamo esportare. GWT continuerà ad essere uno strumento che permette di colmare le lacune di Javascript offrendo un valido ponte tra il linguaggio Java e il mondo Javascript. Oggi le esigenze sono diverse e di fatto Javascript ha meno lacune da colmare; nonostante esistano validi framework come Angular.Js, Ember, Backbone, Ampersend, e affini non possiamo chiudere gli occhi e non fare i conti con l'avvento di ES6, poiché i linguaggi a supporto di questo standard cambieranno il modo di approcciarsi a Javascript. GWT sfruttando Java8 e JSInterop sta anticipando i tempi e di fatto potrà porsi architetturalmente al di sopra di queste evoluzioni di Javascript. Il risultato finale sarà una perfetta amalgama tra i prodotti della compilazione GWT e i moduli Javascript (ESX style) .

Il pezzo mancante

Se pensiamo al fatto che Javascript offre già costrutti a supporto della programmazione funzionale è facile intuire come JsInterop sia il pezzo mancante del ponte di comunicazione tra Java e Javascript. Mi riferisco al fatto che prima risultava non immediato, (ma non impossibile) esportare il risultato della compilazione di GWT da un tipo Java a un namespace Javascript. Praticamente JsInterop ci aiuta a scrivere codice pulito proprio per esportare le nostre librerie Java e poterle utilizzare in Javascript.

Uno sguardo più da vicino

Questa è la nostra classe java
package it.luigibifulco;

@JsType
public class MyJsType {
  public boolean bool = true;

  public MyJsType(boolean bool) {
    this.bool = bool;
  }

  public String aPublicMethod() {
    return "Hello ";
  }

  public static String aStaticMethd() {
    return "What's the meaning of life?";
  }
}
Ecco come possiamo usarla in Javascript dopo l'export di JsInterop
   var myType = new it.luigibifulco.MyJsType();

   if (myType.bool) {
       alert("Ask me what's the meaning of life...");
   }

L'idea di avere un core ben strutturato e ben definito con un linguaggio fortemente tipizzato come Java e riuscire ad utilizzarlo per creare velocemente funzionalità con un linguaggio scarsamente tipizzato come Javascript non è una grande novità, ma è sicuramente una strategia vincente. Sfortunatamente devo dire che vedo pochi, anzi pochissimi, progetti con una architettura del genere, ciò forse è dovuto alla complessità di manutenzione di uno stack del genere. Con l'avvento di JsInterop credo che gran parte di questi problemi siano risolti poiché la fase di traduzione e di export del modulo Java al modulo Javascript sarà totalmente gestita da GWT .
Lo sviluppatore si dovrà preoccupare solamente di istruire il compilatore per fargli capire cosa deve esportare durante la fase di compilazione. Abbiamo già visto infatti l'annotation @JsType che praticamente permette di marcare tutte le nostre classi che saranno esportate in un namespace Javascript, dove il namespace di default è il GLOBAL namespace.

In realtà esistono altre annotation di JsInterop per esportare proprietà metodi e perfino per non esportare alcuni metodi e proprietà di un JsType. Vediamole più da vicino.

@JsType

Come vi ho già detto l'annotation @JsType permette di istruire il compilatore per esporre un tipo Java al mondo Javascript. Una caratteristica interessante di @JsType è che non segue l'ereditarietà dei tipi Java, quindi bisogna esplicitamente annotare anche i tipi di una classe di figlia nonostante estenda una classe padre già annotata con JsType. JSType in presenza di ereditarietà si comporta piuttosto bene. Ad esempio:

Supponendo di avere questo scenario
package it.luigibifulco;

@JsType
public class Parent {

 public void String parentMethod() {
   return "parentMethod";
 }

 public static Child createChild() {
   return new Child();
 }

}

public class Child extends Parent {

 public void String childMethod() {
   return "childMethod";
 }

}
Il risultato sarà il seguente
var child = it.luigibifulco.Parent.createChild();

child.parentMethod() //it works 

child.childMethod(); //WARNING breaking code it will not works

In pratica il compilatore si preoccuperà di esportare solo ciò che gli abbiamo suggerito e avendo dichiarato i metodi della super classe come pubblici si è preoccupato di esportarli in automatico. Riusciremo quindi a creare un tipo della classe figlia poiché viene utilizzato da un metodo della classe padre ma non riusciremo ad invocare il metodo della classe figlia poiché non abbiamo mai istruito il compilatore di dover esportare anche la class Child. A rigor di logica mi sembra che questo tipo di approccio non faccia una piega :).

@JsMethod, @JsProperty, @JsConstructor

Come avete potuto notare con @JsType possiamo esportare tutto quello che abbiamo dichiarato nella nostra classe. Se vogliamo esportare solo una parte del tipo Java verso il mondo Javascript ci vengono in aiuto le altre annotation di JSInterop per annotare solo alcune pezzi di una classe. @JsMethod da utilizzare per esportare un pezzo come metodo, @JsProperty da utilizzare per esportarli come attributi della nostra classe Java e infine @JsConstructor per esportare un metodo o un costruttore java come costruttore Javascript.

Ad esempio:
package it.luigibifulco;

public class Vehicle {

 @JsProperty
 public int wheels;

 @JsConstructor
 public Vehicle(int wheels) {
   this.wheels = wheels;
 }

 @JsMethod
 public String modelType() {
   return "Generic Model";
 }

 @JsMethod
 public static String start() {
   return "Vehicle started";
 }

 public int getWheels() { return this.wheels;}

}
La chiamata al metodo getWheels fallirà perché non è stato annotato e la classe Vehicle non è annotata con @JsType
var car = new it.luigibifulco.Vehicle(4);

var wheels = car.wheels; //it will work

wheels = car.getWheels(); //it will NOT work

In realtà il mapping non è uno-a-uno infatti JsInterop ci permette di esportare metodi Java come proprietà Javascript o come costruttori Javascript.

@JsIgnore

JsIgnore ci torna utile se vogliamo andare per esclusione nel decidere cosa esportare o meno da un tipo Java.

Utilizzando questo approccio:
package it.luigibifulco;

@JsType
public class Vehicle {

 public int wheels;

 public Vehicle(int wheels) {
   this.wheels = wheels;
 }

 public String modelType() {
   return "Generic Model";
 }

 public static String start() {
   return "Vehicle started";
 }

 @JsIgnore
 public int getWheels() { return this.wheels;}

}

abbiamo di fatto ottenuto lo stesso risultato di prima con due sole annotation.

JsFunction

Una annotation degna di nota è la @JsFunction che ci permette di esportare una interfaccia Java come una funzione Javascript e che potrà essere utilizzata come closure in Javascript quando dichiariamo come parametro di input l'interfaccia Java. Questo approccio è davvero semplice ed efficace poiché abbiamo la possibilità di scrivere un contratto tra chiamante e funzione chiamata. Un'aspetto davvero interessante è che abbiamo la possibilità di iniettare dei valori alla closure. Non ci avete capito nulla? Niente paura l'esempio che vi mostro di seguito vi chiarirà tutto:

Java

@JsFunction
interface CommandFunction {

  int exec(int requestNumer);

}

@JsType
class Executor {

  public static int action(CommandFunction cmd) {
     return cmd.exec(40);
  }

}
Javascript
it.luigibifulco.Executor.action(
  function(cmdId) {     
     console.log(cmdId); //it will print 40
     return commandExecuted();
    }); //

  function commandExecuted(){
    return "0";
  }

Per semplicità in questo caso abbiamo iniettato, banalmente un CmdID del comando, avrebbe molto senso sfruttare questa caratteristica per iniettare un gestore degli eventi ad un controller :) .

Namespace

Ogni annotation di JsInterop offre un parametro namespace che ci permette di creare dei veri e propri moduli Javascript e in generale di gestire il modo in cui i nostri tipi Java saranno esportati in Javascript.

@JsType(name = "MyType", namespace = "lb")

In questo modo si può fare in modo che la classe MyType sia utilizzabile dal namespace lb

lb.Mytype

E non finisce qui! Possiamo creare veri e propri package e mapparli con un namespace javascript sfruttando il file package-info.java:

@jsinterop.annotations.JsPackage(namespace = "lb")
package it.luigibifulco

....attivare la JsInterop

Per avviare la compilazione JsInterop bisogna attivare un flag del compilatore di GWT

-generateJsInteropExports true

Il consiglio che posso darvi è provarla subito. Le impressioni sono davvero buone, GWT+JsInterop allenta il gap esistente tra Java e Javascript diminuendo l'effort di sviluppo e permettendo di integrare moduli Java e moduli Javscript già esistenti. Prima di creare questo articolo ero indeciso se usare Typescript o GWT ma con JsInterop ogni dubbio è stato fugato.


Potrebbe interessarti anche...


  • submit to reddit
blog comments powered by Disqus