Java 8 Programmiermethodik Eva Zangerle Universität Innsbruck Java 8 (1) • Java 8 wurde von Oracle am 18. März 2014 veröffentlicht • Damit wurden einige Features umgesetzt, welche ursprünglich schon für Java 7 geplant waren (etwa Lambdas) • Neben Bugfixes wurden weitreichende Änderungen an der Java-Syntax vorgenommen • Seit JDK 1.5 ist Java 8 das wohl umfangreichste Release Hier werden nur die wichtigsten Änderungen/Erweiterungen aufgeführt Programmiermethodik - Java 8 2 Java 8 (2) • Neu in Java 8 Lambdas Default Methoden Streams Optional Type Annotations Repeating Annotations • Änderungen mit Java 8 Nashorn (ersetzt Rhino) Date & Time APIs JavaFX Metaspace (Garbage Collection) Programmiermethodik - Java 8 3 Funktionale Programmierung in Java Funktionale Programmierung • Programm wird als Aneinanderreihung von mathematischen Funktionen repräsentiert • Daten sind stets unveränderbar • Frei von Seiteneffekten • Funktionen können weitere Funktionen übergeben werden (Code-as-Data) • Vorteile: Kürzerer, übersichtlicherer Code Besser parallelisierbar Seiteneffekte Lambdas - Einführung • Bekannt als „Project Lambda“ • Hauptfeature von Java 8 • Dient vor allem der Vereinfachung des Programmcodes etwa beim Sortieren von Collections • Lambdas sind „objektlose Methoden“ • Kommen nur einmal im Code vor • Angelehnt an funktionale Programmierung (Programme bestehen nur aus Funktionen, keine Seiteneffekte) • Funktionen als Parameter • Anonyme innere Klasse kapselt diese Verhalten Programmiermethodik - Java 8 6 Lambdas (1) • Syntax: parameters -> body (argument list) -> code • Beispiele: (String name, Address address) -> [Implementierung] (name, adress) -> [Implementierung] Type inference name -> [Implementierung] () -> return 42 • Weitere Information unter http://openjdk.java.net/projects/lambda/ Programmiermethodik – Java 8 7 Lambdas (2) • Beispiele: (String s1, String s2) -> {return s2.length() - s1.length();} (String str) -> System.out.println(str) • Parameter-Typ-Deklaration ist optional; bei einem einzigen Parameter auch die Klammerung: str -> System.out.println(str) () -> System.out.println(this) • Bei Verwendung eines einzigen Statements im Body, kann die Block-Definition und return weggelassen werden: (s1, s2) -> s2.length() - s1.length() Programmiermethodik - Java 8 9 Lambdas (3) • Comparator mit inner class: Comparator<String> ignoreCaseComparator_old = new Comparator<String>() { public int compare(String s1, String s2) { return (s1.toLowerCase().compareTo(s2.toLowerCase())); } }; • Comparator mit Lambda: Comparator<String> ignoreCaseComparator_new = (String s1, String s2) -> (s1.toLowerCase().compareTo(s2.toLowerCase())); Programmiermethodik – Java 8 10 Lambdas (4) public class RunnableTest { public static void main(String[] args) { System.out.println("=== RunnableTest ==="); // Anonymous Runnable Runnable r1 = new Runnable(){ @Override public void run(){ System.out.println("Hello world one!"); } }; // Lambda Runnable Runnable r2 = () -> System.out.println("Hello world two!"); r1.run(); r2.run(); } } 11 Lambdas (5) List<Person> list = list.add(new list.add(new list.add(new new ArrayList<Person>(); Person("max", "mustermann", 20)); Person("jane", "doe", 44)); Person("john", "doe", 55)); Collections.sort(list, (Person p1, Person p2) -> p1.getLastName().compareTo(p2.getLastName())); list.forEach(x -> x.increaseAge()); Programmiermethodik – Java 8 12 Methoden-Referenzen • Durch die Einführung von Lambdas werden MethodenReferenzen möglich. Diese sind definiert für: Statische Methoden Instanz-Methoden Methoden einer bestimmten Instanz Konstruktoren (etwa TreeSet::new) • Methoden-Referenzen werden mit Hilfe von „::“ angegeben Programmiermethodik - Java 8 13 Methoden-Referenzen • Beispiel (statt list.forEach(x -> x.increaseAge()): list.forEach(Person::increaseAge); • Ausgabe aller Listenelemente: list.forEach(System.out::println) • Verschachteltes Beispiel (mit java.nio.file.Files.lines (NonBlocking IO)): Files.lines(Paths.get("Nio.java")).map(String::trim) .forEach(System.out::println); • Weitere Informationen: http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html Programmiermethodik – Java 8 14 Functional Interfaces (1) • Als weitere Erweiterung der Lambdas gibt es in Java 8 so genannte „Functional Interfaces“. • Das sind Interfaces, welche genau eine (abstrakte) Methode beinhalten (Ausnahme sind geerbte Methoden von Objekt sein – also etwa toString()) • Auch Single Abstract Method interfaces (SAM) genannt • Beispiele: Callable, Comparator, Runnable Programmiermethodik - Java 8 15 Functional Interfaces (2) • Lambdas können stets nur eine Methode definieren -> durch Functional interfaces kann sichergestellt werden, dass Lambdas verwendet werden können. • Die Annotation „@FunctionalInterface“ kann auf solchen Interfaces verwendet werden, um CompilerFehlermeldungen zu erhalten. Programmiermethodik – Java 8 16 Functional Interfaces (2) @FunctionalInterface public interface Displayable { public display(); } public class DisplayableImplementation { public static void main(String[] args) { Displayable disp_anonymous = new Displayable() { @Override public void display() { System.out.println("anonymous inner class"); } }; disp_anonymous.display(); Displayable disp_lambda = () -> System.out.println("lambda expression"); } } Programmiermethodik - Java 8 17 Streams (1) • Mit Java5 wurde eine neue for-Schleife eingeführt: List<String> myList = Arrays.asList("e0", "e1", ..., "e999"); for (String e: myList) { System.out.println(e); } • Diese forEach-Schleife hat den Nachteil, dass die Elemente nicht parallel, sondern sequentiell abgearbeitet werden (man müsste zuerst händisch das Array in Teile zerlegen und diese dann parallel abarbeiten) • Aus diesem Grund gibt es in Java 8 Streams und eine weitere forEach Schleife (das Interface Iterable wurde um eine default Methode „forEach“ erweitert) Programmiermethodik - Java 8 18 Streams (2) • forEach-Schleife auf Streams basiert auf dem Consumable functional interface, das die Implementierung einer accept –Methode verlangt: • Beispiele mit als Lambda implementiertem accept(): list.forEach(x -> x.increaseAge()); list.forEach(System.out::println) Programmiermethodik – Java 8 19 Streams (3) • Streams können auch ohne forEach verwendet werden. Dazu gibt es etwa die Collections.stream, Arrays.stream oder die Collections.parallelStream – Methoden • Streams wurden für viele Anwendungen definiert: Für das Arbeiten mit Dateien und Verzeichnissen (etwa mit Files.lines) Patterns (bei RegularExpressions) können gestreamt werden Die Klasse Stream kann für beliebige Typen verwendet werden (mit Hilfe der Methode Stream.of()) Unendliche Streams sind definiert (Stream.iterate(i, i -> i+1)) Programmiermethodik - Java 8 20 Streams (4) • Map/Filter/Reduce – Verfahren können nun einfach umgesetzt werden (und auch parallelisiert werden): Input-Daten [Ops]* [Terminal Op] Output-Daten • Methoden, die angewendet werden können (Auszug): Map Filter Aggregationen wie Sum … • Methoden, die am Schluss auf den Stream angewendet werden können (terminal ops): forEach Reduce Programmiermethodik - Java 8 21 Streams (4) List<Transaction> groceryTransactions = new Arraylist<>(); for(Transaction t: transactions){ if(t.getType() == Transaction.GROCERY){ groceryTransactions.add(t); } } Collections.sort(groceryTransactions, new Comparator(){ public int compare(Transaction t1, Transaction t2){ return t2.getValue().compareTo(t1.getValue()); } }); List<Integer> transactionIds = new ArrayList<>(); for(Transaction t: groceryTransactions){ transactionsIds.add(t.getId()); } Beispiel und Grafiken entnommen aus http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams2177646.html Programmiermethodik – Java 8 22 Streams (4) List<Integer> transactionsIds = transactions.stream() .filter(t -> t.getType() == Transaction.GROCERY) .sorted(comparing(Transaction::getValue).reversed()) .map(Transaction::getId) .collect(toList()); Beispiel und Grafiken entnommen aus http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams2177646.html Programmiermethodik – Java 8 23 Streams (5) Beispiel und Grafiken entnommen aus http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams2177646.html Programmiermethodik – Java 8 24 Optional (1) • Optional wurde als neue Klasse eingeführt, um NullPointerExceptions vorzubeugen. • Angelehnt an funktionale Programmiersprachen. • Beispiel (ohne Optional): String version = computer.getSoundcard().getUSB().getVersion(); • Alternative (?): String version = "UNKNOWN"; if(computer != null) { Soundcard soundcard = computer.getSoundcard(); if(soundcard != null) { USB usb = soundcard.getUSB(); if(usb != null) { version = usb.getVersion(); } } } Beispiel entnommen aus http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html Programmiermethodik - Java 8 25 Optional (2) public class Computer { private Optional<Soundcard> soundcard; public Optional<Soundcard> getSoundcard() { ... } ... } public class Soundcard { private Optional<USB> usb; public Optional<USB> getUSB() { ... } } public class USB{ public String getVersion(){ ... } } Beispiel entnommen aus http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html Programmiermethodik – Java 8 26 Optional (3) • Optional.of() kann als Wrapper um einen Wert verwendet werden, um null zu vermeiden • Optional.isPresent() kann für bisherige Prüfungen verwendet werden • Optional.ifPresent(Consumer) wird verwendet, um eine Aktion durchzuführen, wenn Werte gesetzt sind: optionalSoundcard.map(Soundcard::getUSB) .filter(usb -> "3.0".equals(usb.getVersion()) .ifPresent(() -> System.out.println("ok")); Programmiermethodik - Java 8 27 Java 8: Nicht-funktionale Erweiterungen Default Methoden (1) • Default Methoden sind Teile von Interfaces, welche eine Implementierung beinhalten. • Neben Default-Methoden, können auch static Methoden ausprogrammiert werden. • Der Hauptunterschied zu abstrakten Klassen ist darin zu sehen, dass es große Unterschiede bei der Vererbung gibt (eine Klasse kann mehrere Interfaces implementieren). • Es kommt zu einem Compiler-Fehler, wenn eine Subklasse zwei Interfaces implementiert, welche Default Methoden mit gleichem Namen beinhalten (siehe nächstes Beispiel). Programmiermethodik - Java 8 29 Default Methoden (2) Beispiel: public interface A { default void foo() { System.out.println("Calling A.foo()"); } } public interface B { default void foo() { System.out.println("Calling B.foo()"); } } public class MySubclass implements A, B { // führt zu Compiler-Fehler } Programmiermethodik - Java 8 30 Default Methoden (3) • Beispiel: public interface A { default void foo() { System.out.println("Calling A.foo()"); } } public interface B { default void foo() { System.out.println("Calling B.foo()"); } } public class MySubclass implements A, B { default void foo() { A.super.foo(); // OK (jede Implementierung ist ok) } } Programmiermethodik - Java 8 31 Type Annotations • Type Annotations sind Annotationen, welche überall dort verwendet werden können, wo ansonsten Typen stehen. • Gedacht sind Type Annotations, um eine bessere Validierung zu ermöglichen (dafür gibt es etwa Frameworks, welche die Überprüfung übernehmen; z.B. das Checker Framework). • Beispiel: final String myString = (@NonNull String) str; • Weitere Annotations: @ReadOnly, @NonNegative, Range Checks, etc. Programmiermethodik - Java 8 32 Repeating Annotations • Mit Type Annotations wurden auch Repeating Annotations eingeführt. Damit kann ab Java8 kann die selbe Annotation mehrfach für eine Methode/Typ/Klasse verwendet werden • Bei selbst definierten Annotationen verwendet man dafür @Repeatable • Beispiel (Cron-like): @Schedule(dayOfWeek = “Friday") @Schedule(dayOfMonth = “last”) public void doPeriodicCleanup() { ... } • Weitere Informationen: http://docs.oracle.com/javase/tutorial/java/annotations/repeating.html Programmiermethodik - Java 8 33 Änderungen mit Java 8 Nashorn (1) • Nashorn ersetzt Rhino als JavaScript-Interpreter. Damit wird es möglich, JavaScript Code auf der JVM auszuführen. • Nashorn verwendet invokedynamic und ermöglicht damit eine bessere Performance (3-5 mal). • Das Kommandozeilentool „jjs“ wurde in Java 8 eingeführt und ruft die Nashorn-Engine auf. • Die Verwendung ist auch direkt im Java – Code möglich (siehe Beispiel). Programmiermethodik - Java 8 35 Nashorn (2) • Beispiel: import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; //… ScriptEngineManager engineManager = new ScriptEngineManager(); ScriptEngine engine = engineManager.getEngineByName("nashorn"); //… engine.eval("function myPrint(s) { print(s); }"); engine.eval("myPrint('Hello Nashorn');"); Programmiermethodik - Java 8 36 Nashorn (3) • Beispiel (mit invokeFunction): import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; //… ScriptEngineManager engineManager = new ScriptEngineManager(); ScriptEngine engine = engineManager.getEngineByName("nashorn"); //… Invocable inv = (Invocable) engine; engine.eval("function myPrint(s) { print(s); }"); inv.invokeFunction("myPrint", "Hello Nashorn2"); Programmiermethodik - Java 8 37 Date & Time API • Als ein Nachteil von Java kann die komplexe Arbeitsweise mit Datums- und Zeitfunktionen angesehen werden (etwa ist SimpleDateFormat nicht thread-safe) • Deshalb wurde eine neue API eingeführt, welche an die Library „Yoda Time“ angelehnt ist. • Für Datums- und Zeitoperationen können nun neue Klassen verwendet werden: LocalDate – Datum (also Tag, Monat, Jahr) LocalTime – Zeit (innerhalb eines Tages) LocalDateTime – Datum und Zeit Programmiermethodik - Java 8 38 JavaFX (1) • Für grafische Oberflächen sollte kein Swing mehr verwendet werden (AWT ist sogar schon länger als „deprecated“ eingestuft) • Ab Java8 finden sich alle benötigten Teile für JavaFX im JRE (gilt für Oracle Java, nicht für OpenJDK). • Damit ist nun JavaFX8 die empfohlene Methode, grafische (Desktop)Applikationen umzusetzen. • JavaFX8 nutzt verstärkt Lambdas und ermöglicht somit eine einfachere Programmierung von GUIs Programmiermethodik - Java 8 39 JavaFX (2) • Das Theme „Modena“ ist nun die Standardeinstellung, womit Oberflächen moderner aussehen als mit JavaFX2 (und/oder Swing, AWT) • Mit Java 8 erhielt JavaFX vollen 3D Support. • Weitere Informationen http://www.oracle.com/technetwork/java/javase/overview/javafxoverview-2158620.html Programmiermethodik - Java 8 40 Metaspace • PermGen wurde abgelöst durch den so genannten Metaspace. Der nun verwendete Metaspace wird im nativem Speicher verwaltet. • Die Parameter „XX:PermSize” und “XX:MaxPermSize” sind nicht mehr verfügbar und werden ignoriert • Einstellungen für den Metaspace können vorgenommen werden (etwa ein Größenlimit), sind aber nur in Ausnahmefällen notwendig Programmiermethodik - Java 8 41 Ausblick Ausblick • Java 9-Release für Herbst 2016 geplant. • Vermutete, neue Features: Project Jigsaw - Modulsystem Neue HTTP2-Client-API Kulla: jshell als Read-Eval-Print-Loop Einfachen Programmcode direkt ausführen Neuer Garbage-Collector (G1) Neue API zur Verwaltung von Betriebssystem-Prozessen JSON-API (Parsen von JSON-Streams) (mittlerweile fragwürdig) Programmiermethodik - Java 8 43 Literatur Literatur • [Davis 2014] Adam Davis, What’s new in Java8, 2014 • (Project Lambda) http://openjdk.java.net/projects/lambda/ • (Method References) http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html • (Optional) http://www.oracle.com/technetwork/articles/java/java8-optional2175753.html • (Repeating Annotations) http://docs.oracle.com/javase/tutorial/java/annotations/repeating.html • (Nashorn) http://www.oracle.com/technetwork/articles/java/jf14-nashorn2126515.html • (JavaFX) http://www.oracle.com/technetwork/java/javase/overview/javafx-overview2158620.html • (Project Jigsaw) http://openjdk.java.net/projects/jigsaw/ Programmiermethodik - Java 8 45