Este primăvară... vremea schimbărilor şi a speranţelor... Oracle contribuie la toate acestea cu o nouă versiune a platformei Java standard. Este vorba de versiunea 8, lansată în martie 2014. Începând cu acest număr al revistei Today Software Magazine doresc să modific stilul articolelor pe care le scriu. Nu pot spune că abandonez ideea recenziilor, care reprezintă o sursă importantă de popularizare a cărţilor valoroase din biblioteca IT, dar voi adăuga şi articole cu un grad mai mare de tehnicitate. Urmăresc prin aceasta ca cititorii revistei să fie „stârniţi” în a descoperi ce este nou şi performant în lumea dezvoltării de aplicaţii software.
Mă bucur foarte mult că acest număr este lansat şi în Braşov şi, deşi este prima lansare aici, sper să fie urmată de multe altele. Braşovul are un potenţial enorm, iar eu iubesc incredibil acest oraş.
Java SE8 este considerată revoluţionară, prin câteva dintre noile caracteristici introduse. Programată iniţial pentru luna septembrie 2013, lansarea a fost amânată pentru martie 2014. Motivele sunt numeroase, dar ţin în special de corectarea bug-urilor şi de îmbunătăţirea securităţii, mai ales pe parte de client, având ca principal motiv JavaFX.
Multe sunt modificările şi adăugările făcute în limbaj, dar probabil cea mai spectaculoasă este introducerea abilităţilor lambda. Acestea sunt văzute ca un important beneficiu în programarea paralelă. De fapt, eforturile pentru creşterea performanţei în programarea paralelă s-au văzut încă din versiunea 7, introducerea framework-ului Fork-Join este un singur exemplu.
În prima parte a articolului mă voi orienta cu precădere spre lambda expresii, în partea finală voi prezenta succint un engine JavaScript nou nouţ, pentru ca în articolele următoare doresc să aduc în discuţie şi alte subiecte legate de Java SE٨.
O funcţie lambda (funcţie anonimă) este o funcţie definită şi apelată fără a fi legată de un identificator. Funcţiile lambda sunt o formă de funcţii ,,incuibate” (nested functions) în sensul că permit accesul la variabilele din domeniul funcţiei în care sunt conţinute.
Funcţiile anonime au fost introduse de către Alonzo Church în anul 1936, în teoria sa despre calculele lambda.
În limbajele de programare, funcţiile anonime sunt implementate din anul 1958 ca parte a limbajului Lisp. În unele limbajele orientate pe obiect, precum Java, apar concepte similare, precum clasele anonime. Abia în versiunea 8 a limbajului Java sunt adăugate şi funcţiile anonime. Alte limbaje, precum C#, JavaScript, Perl, Python, Ruby ofereau demult suport pentru acest concept.
Lambda expresiile ne permit să creăm instanţe ale claselor cu o singură metodă într-un mod mult mai compact.
O lambda expresie constă:
->
,O interfaţă funcţională (functional interface) este orice interfaţă ce conţine doar o metodă abstractă. Din această cauză putem omite numele metodei atunci când implementăm interfaţa şi putem elimina folosirea claselor anonime. În locul lor vom avea lambda expresii. O interfaţă funcţională este anotată cu @FunctionalInterface
. Pentru a înţelege modul în care se lucrează cu lambda expresii am construit un mic exemplu prin care am creat colecţii de obiecte sortate după diverse criterii. Implementarea interfeţei Comparator
a fost făcută într-o clasă anonimă, folosind lambda expresii. Implementarea cu lambda expresii a fost posibilă pentru că în versiunea 8 Comparator este anotată cu @FunctionalInterface
.
Elementul de bază al colecţiei este clasa Product
, care este o clasă POJO cu getter
-i și setter
-i. Clasa conţine două implementări anonime ale comparatorului, determinând sortarea crescătoare respectiv descrescătoare a elementelor colecţiei.
package model;
import java.util.Comparator;
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public void printProduct() {
System.out.println(this.toString());
}
@Override
public String toString() {
return "Product [name=" + name + ", price=" + price + "]";
}
public static Comparator
ascendingPrice = (p1, p2) -> {
return p1.getPrice() - p2.getPrice();
};
public static Comparator
descendingPrice = (p1, p2) -> {
return p2.getPrice() - p1.getPrice();
}
}
Clasa de test va aduce ceva în plus faţă de o clasă cunoscută până în versiunea 8. Procesarea colecţiei nu se va face cu un foreach
clasic. Ca parte a API-ului Collections avem noul API java.util.stream
ce oferă suport pentru operaţii funcţionale pe stream-uri de elemente. În exemplul nostru vom folosi o interfaţă de bază a acestui API şi anume Consumer
, care reprezintă o operaţie ce acceptă un singur argument de intrare şi nu returnează ceva. Cu Consumer
vom putea folosi lambda expresii:
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
import model.Product;
public class TestLambda {
public static void processProducts(
Set products, Consumer block) {
for (Product p : products) {
block.accept(p);
}
}
public static void main(String[] args) {
Product p1 = new Product();
p1.setName("onion");
p1.setPrice(10);
Product p2 = new Product();
p2.setName("tomato");
p2.setPrice(20);
Set ascendingPriceProducts = new TreeSet<>(Product.ascendingPrice);
ascendingPriceProducts.add(p1);
ascendingPriceProducts.add(p2);
System.out.println("In ascending order:");
processProducts(ascendingPriceProducts, p -> p.printProduct());
Set descendingPriceProducts = new TreeSet<>(Product.descendingPrice);
descendingPriceProducts.add(p1);
descendingPriceProducts.add(p2);
System.out.println("In descending order:”);
processProducts(descendingPriceProducts, p -> p.printProduct());
}
}
Ca urmare a folosirii API-ului stream operaţiile efectuate pe o colecţie pot fi mult mai complexe decât cele ilustrate în exemplu şi anume: filtrarea după un predicat de selecţie, maparea obiectului filtrat, respectiv executarea unei acţiuni pe fiecare obiect mapat. Eu am prezentat doar ultima operaţie. Acestea se numesc operaţii agregat.
Observaţia pe care vreau să o fac codului anterior este că implementarea comparatorului ţine loc de suprascriere a funcţiei equals()
, fapt ce poate fi dovedit prin modificarea, în cod, la valori egale a preţului.
Pe lângă lambda expresii, o caracteristică importantă, evident alături de modificările sintactice şi introducerea de API-uri noi, este dezvoltarea engine-ului JavaScript Nashorn (se pronunţă naz-horn). Prin acesta se pot integra script-uri JavaScript în codul Java clasic. Acest engine se bazează pe standardul ECMAScript 262. Este un engine scris complet de la zero având ca obiectiv creşterea performanţei. Este astfel, complet diferit faţă de engine-ul deja existent Rhino.
Voi da doar un mic exemplu de folosire a acestui engine, urmând ca în viitor să prezint mai multe detalii:
import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a Nashorn script engine
ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript statement
try {
engine.eval(“print(‘Hello, World!’);”);
} catch (final ScriptException se) {
se.printStackTrace();
}
}
}
Rulând acest exemplu vom obține la consolă "Hello, World!".
Ca ultimă observaţie, am folosit pentru editare Eclipse Kepler iar de pe Marketplace am adus Eclipse Java, 8 Support (for Kepler SR2) JDT, PDE 1.0.0. Aceasta, până va apare Eclipse Luna (probabil în mai). Ca versiune de Java am utilizat jdk 1.8.0_05.
Sper că v-am stârnit interesul pentru Java 8 şi ca de obicei aştept cu mare plăcere discuţiile cu cei interesaţi.
Noile mele date de contact sunt: Silviu.Dumitrescu@accesa.eu