Java

Standard Edition

8

Auth Gábor

Agenda

A Java SE 8 viharos története
Base64 Encoding & Decoding
Nashorn JavaScript Engine
Remove the Permanent Generation
Date & Time API
Parallel Array Sorting
Bulk Data Operations for Collections
Lambda Expressions

A Java SE 8 története

"... JDK 8 is due in late 2012 ..."
"... Project Jigsaw: Late for the train ... (2012-07-17)"
"... Java SE 8 are moving forward [...] in summer 2013 ..."
"... GA: September 2013 ..."


De végre elkészült! :)

Base64 Encoding & Decoding

A rég várt osztály:
import java.util.Base64;

import java.util.Base64

Base64.Encoder encoder = Base64.getEncoder();
byte[] clear = "JUM 1403".getBytes("UTF-8");
String encoded = encoder.encodeToString(clear);
System.out.println(encoded);

Base64.Decoder decoder = Base64.getDecoder();
byte[] decoded = decoder.decode("SlVNIDE0MDM=");
System.out.println(new String(decoded, "UTF-8"));
                

import java.util.Base64

SlVNIDE0MDM=
JUM 1403
                

Nashorn JavaScript Engine

A Rhino nevezetű JavaScript motort a Nashorn váltja, ez sebességben, memóriafogyasztásban és JavaScript funkcionalitásban hoz javulást, a használata azonos módon történik.

import javax.script.*

ScriptEngineManager manager = new ScriptEngineManager();
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
    System.out.println("engineName=" + factory.getEngineName());
    System.out.println("engineVersion=" + factory.getEngineVersion());
    System.out.println("languageName=" + factory.getLanguageName());
    System.out.println("extensions=" + factory.getExtensions());
    System.out.println("languageVersion=" + factory.getLanguageVersion());
    System.out.println("names=" + factory.getNames());
}                    
                

Java SE 7

engineName=Mozilla Rhino
engineVersion=1.7 release 3 PRERELEASE
languageName=ECMAScript
extensions=[js]
languageVersion=1.8
names=[js, rhino, JavaScript, javascript, ECMAScript, ecmascript]
                

Java SE 8

engineName=Oracle Nashorn
vengineVersion=1.8.0
languageName=ECMAScript
extensions=[js]
languageVersion=ECMA - 262 Edition 5.1
names=[nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript]
                

import javax.script.*

Fontos, hogy "nashorn" legyen a motor neve!
ScriptEngine jsEngine = manager.getEngineByName("nashorn");
try {
    jsEngine.eval("print('Hello World');");
} catch (ScriptException ex) {
    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
} 
                

import javax.script.*

Hello World
                
vagy NullPointerException, ha "nashorn"-t használunk 1.8.0 alatt, vagy "rhino"-t 1.8.0 és felette... lesz még ebből baj... :)

Remove the Permanent Generation

Végre valóban nem lesz több PermGen space hiba!

Remove the Permanent Generation

...és persze a PermGen beállítások se fontosak többé:
VM warning: ignoring option MaxPermSize=384m; support was removed in 8.0
VM warning: ignoring option PermSize=32m; support was removed in 8.0
                

Date & Time API

A Base64 en- és dekódoló modulhoz hasonlóan a dátum- és időkezelés a Java egyik nagyon fájdalmas része volt mostanáig, mivel megérkezett az új dátum és idő kezelés!

Clock

A régi osztályok megmaradtak kompatibilitási okokból, ezért a Clock osztály került bevezetésre:
System.out.println(Clock.systemUTC());
System.out.println(Clock.systemDefaultZone());
System.out.println(Clock.systemDefaultZone().millis());
                
SystemClock[Z]
SystemClock[Europe/Budapest]
1395215915437
                

ZoneId

System.out.println(ZoneId.systemDefault()); 
System.out.println(Clock.system(ZoneId.of("Europe/London"))); 
                
Europe/Budapest
SystemClock[Europe/London]
                

LocalDate

LocalDate date = LocalDate.now();
int year = date.getYear();
int month = date.getMonthValue();
int day = date.getDayOfMonth();
System.out.println(year + "-" + month + "-" + day);
                
2014-3-19
                

LocalDate

LocalDate today = LocalDate.now();
System.out.println(today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2));
                
2014-03-29
                

LocalDateTime

LocalDateTime today = LocalDateTime.now();
System.out.println(today);
System.out.println(today.minusWeeks(2).minus(2, ChronoUnit.HOURS));
                
2014-03-19T14:02:45.581
2014-03-05T12:02:45.581
                

Duration

Instant t1 = Instant.now();
// process
Instant t2 = Instant.now();

System.out.println(Duration.between(t1, t2).toMillis());
                
1068
                

Period

LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1978, Month.MARCH, 27);
Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.println("You are " + p.getYears() + " years, " + p.getMonths()
        + " months, and " + p.getDays()
        + " days old. (" + p2 + " days total)");
                
You are 35 years, 11 months, and 20 days old. (13141 days total)
                

Parallel Array Sorting

Egyszerűnek tűnő újítás, de sok munka van mögötte, ami csak nagy adamennyiségnél lesz hasznos, az alábbi hat adatra nem:
int[] prices = new int[]{4,8,3,6,5,1};
System.out.println(Arrays.toString(prices));
Arrays.parallelSort(prices);
System.out.println(Arrays.toString(prices));
                
[4, 8, 3, 6, 5, 1]
[1, 3, 4, 5, 6, 8]
                

Bulk Data Operations for Collections

A Lambda egyik "kézzelfogható" nyoma az API változásokat tekintve, ugyanis a "stream" elv mögé csak a Lambda tud olyan egyszerű és tiszta kódot tenni, hogy a fejlesztőnek ne menjen el az életkedve... :)

A "stream" elv

A "stream" lényege, hogy ha egy listán végigiterálunk valamilyen céllal, akkor ezt ciklus helyett tehetjük egy lépésben Lambda segítségével.
A "stream" nem foglal memóriát és könnyen párhuzamosítható a feldolgozása. A különbség olyan, mint az InputStream és a byte[] között.

stream().filter

Például szűrni szeretnénk:
List<String> names = new ArrayList<>(
        Arrays.asList("Aladár","Béla","Cecil","Dénes","Elemér"));
List<String> five = names.stream().filter(
        s -> s.length() == 5).collect(Collectors.toList());
System.out.println(five);
                
[Cecil, Dénes]
                

Lambda Expressions

A Java 8 egyik – illetve a Project Jigsaw elkaszálása óta az egyetlen – nagy újítása a Lambda expressions beépítése a nyelvi eszközök közé.
A funkcionális nyelvekkel együtt került képbe a lambda-kalkulus elnevezés, amely lehetővé teszi, hogy paraméterként függvényt lehessen átadni egy metódus hívásánál, ezzel egyszerűbben lehet leírni azt a feladatot, amelyet eddig a név nélküli belső osztályokkal oldottunk meg (anonymous inner class).

Lambda Expressions

Lássunk egy klasszikus példát, egy listát szeretnénk rendezni egy Comparable interfészt kiterjesztő saját osztállyal.
Collections.sort(list, new Comparator<String>()
{
    public int compare(String o1, String o2)
    {
        return random.nextInt();
    }
});
                
Collections.sort(list, (o1, o2) -> random.nextInt());
                

Lambda Expressions

Ezért fogjuk szeretni a Lambda expressions használatát, bár szoknia kell a Java utóbbi 10 évéhez szokott szemnek az ilyen sorokat, de meg lehet tanulni, ahogy az annotációkat és a generics-et is megtanultuk... :)
A gyári Lambda expressions vonzó lehetőség, de ez a játék sokkal érdekesebb saját függvénytörzsekkel, s ha létrehozunk saját Lambda expressions-ön alapuló megoldásokat, akkor jobban megértjük a működését is.

Lambda Expressions

Először is szükségünk van egy interfészre, amelyből majd Lambda expressions lesz, legyen a példa egy szűrő, amely a kapott objektum értéke szerint majd egy igaz vagy egy hamis választ ad:
public interface Filter<T>
{
  boolean filter(T o1);
}
                

Lambda Expressions

Ezek után a fentebb már elkészített LambdaRunExample.java állományban hozzunk létre egy metódust, amely majd elvégzi a szűrést:
public static <T> void applyFilter(List<T> list, Filter<? super T> f)
{
  List<T> removeFromList = new ArrayList<T>();
  for (T item : list)
  {
    if (f.filter(item))
    {
      removeFromList.add(item);
    }
  }
  list.removeAll(removeFromList);
}
                

Lambda Expressions

Amint látjuk, a metódus második paramétereként egy Filter interfészt implementáló osztály egy példányát szeretnénk megkapni, amelyet a for each ciklusban használunk fel, ahol a kapott item példányról döntjük el, hogy ki kell-e szűrnünk a listából vagy sem.

Lambda Expressions

A lényegi rész – maga a Lambda expression – a Collections.sort metódushoz hasonlóan épül fel, egyszerűen megadjuk azt a feltételt, amely szerint a szűrést el szeretnénk végezni, jelen esetben a három karakternél hosszabb szövegeket az applyFilter metódus el fogja távolítani a listából:
LambdaRunExample.applyFilter(list, (item) -> item.length() > 3);
                

Lambda Expressions

Felmerül a kérdés, hogy a fordításkor honnan tudja a fordító, hogy melyik metódusról van szó, s ez a kérdés a Lambda expressions lényege... :) Rontsuk el a Filter interfészt, adjuk hozzá egy új metódust:
public interface Filter<T>
{
  boolean filter(T o1);
  boolean anotherFilter(T o1);
}
                

Lambda Expressions

A LambdaRunExample.java osztályt lefordítva rögtön besír a javac, hogy nem tudja eldönteni, mit is szeretnék tőle, mert az átadott interfészben több olyan metódus is van, amelyet használhatna:
LambdaRunExample.java:19: error: method applyFilter in class
LambdaRunExample cannot be applied to given types;
    LambdaRunExample.applyFilter(list, (item) -> item.length() > 3);
                    ^
  required: List<T>,Filter<? super T>
  found: List<String>,lambda
  reason: multiple non-overriding abstract methods found in interface Filter
  where T is a type-variable:
    T extends Object declared in method <T>
applyFilter(List<T>,Filter<? super T>)
1 error
                

Kérdések? :)


images/qr_code.png
(Presentation by impress.js)