|
|||||||||||||||||||
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | | | | |||||||||||||||||||
|
Effective Java
|
||||||||||||||||||
Ergänzungen an der Sprache und existierenden APIsCollection Literals
Für Java 7 hatte Sun damals die Java Community
gefragt, welche kleineren Ergänzungen an der Programmiersprache nützlich
wären. Im Rahmen des sogenannten „Project Coin“ wurden die Vorschläge
gesammelt und zum Teil für Java 7 umgesetzt. Damals sind Features wie
try-with-resources, switch-over-string und multi-catches dazu gekommen.
Es wurden aber nicht alle Vorschläge berücksichtigt. Eine der Ideen,
die liegen geblieben ist, sind die sogenannten Collection Literals. Dabei
geht es um eine kompakte Syntax für das Erzeugen von unveränderlichen
Collections mit einigen wenigen Elementen. Der damalige Vorschlag sah
so aus:
List<Integer> list = #[ 1, 2, 3 ];
Für Java 9 hat man sich nun eine Lösung
überlegt. Allerdings ist dafür nicht die Sprache geändert worden.
Die oben gezeigte Syntax gibt es nicht. Aber man hat eine Lösung in
der Bibliothek gefunden (siehe /
COLL
/).
Es wird in den Interfaces
List
,
Set
und
Map
statische Factory-Methoden für
das Erzeugen von unveränderlichen Collections geben. Das sieht dann
zum Beispiel so aus:
List<Integer> list = List.of( 1, 2, 3 );
Ähnliche Factory-Methoden gibt es bereits in der Klasse Collections:
// alt: List<String> noElement = Collections.emptyList(); List<String> oneElement = Collections.singletonList("a"); // neu:
List<String> someElements = List.of("a", "b", "c");
Die neuen Factory-Methoden fügen sich also ganz natürlich in die bestehenden
APIs ein.
Kleinere SpracherweiterungenEs gibt eine Reihe kleinerer Spracherweiterung, die so geringfügig sind, dass man sie kaum wahrnehmen wird:• Diamond Operator an anonymen Klassen • Private Interface-Methoden • Underscore als Bezeichner
•
Effectively-final
Variablen in try-with-resources
Diamond Operator an Anonymen Klassen
Der sogenannte “Diamond Operator” wurde
mit Java 7 eingeführt. Er erspart dem Entwickler bei der Initialisierung
eines Objekts von einen parametrisierten Typ ein wenig Schreibarbeit: man
darf beim Konstruktoraufruf leere spitze Klammern (eben den „Diamond
Operator“) verwenden. Das sieht so aus:
List<Long> list = new ArrayList
<>
();
// fine
Leider durfte man den Diamond Operator aber
nicht bei anonymen Klassen verwenden. Das nachfolgende Beispiel hat zu
einer Fehlermeldung beim Kompilieren geführt:
Callable<Long> task = new Callable <> () { // error in Java 7; fine in Java 9 public Long call() { return 42L; }
};
Diese Fehlermeldung wird man in Java 9 nicht mehr bekommen. Der Diamond Operator ist jetzt auch bei anonymen Klassen erlaubt. Private Interface-Methoden
Seit Java 8 gibt es in Interfaces nicht
nur abstrakte Methoden ohne Implementierung, sondern auch Default- und
statische Methoden mit Implementierung. Die Default- und statischen Methoden
durften in Java 8 nur
public
sein, weil
alles in Interfaces automatisch
public
ist. Diese Beschränkung fällt in Java 9 teilweise weg. Statische
und nicht-statische Interface-Methoden dürfen in Java 9 nun auch
private
deklariert werden. Bei den privaten Instanz-Methoden muss aber das Schlüsselwort
default
weglassen, denn sie liefern sie keine Default-Funktionalität für die
implementierenden Klassen, weil sie nicht vererbt werden. Private Interface-Methoden
dienen ausschließlich als Hilfsmittel für die Implementierung der anderen
Methoden im Interface.
Hier eine Übersicht darüber, welche Interface-Methoden
private
sein dürfen und welche nicht:
interface Interface { public void abstractMethod() ; // since Java 1
private void notAllowed()
; // error
public default void defaultMethod() {} // since Java 8 private default void notAllowed() {} // error
private void privateMethod()
{} // since in Java 9
public static void staticMethod() {} // since Java 8 private static void privateStaticMethod() {} // since Java 9
}
Underscore als BezeichnerBis Java 7 einschließlich war der Underscore „_“ ein gültiger Name z.B. für eine Variable. Das Folgende war also erlaubt:void f( int _ ) { System.out.println( _ );
}
In Java 8 war ein solcher Bezeichner dann
"deprecated". In Java 9 ist der einzelne Underscore nun kein gültiger
Bezeichner mehr und wird vom Compiler als Fehler gemeldet.
Der genaue Grund für das Einkassieren des Underscores ist nicht bekannt. Es wurde im Zusammenhang mit der Definition der Syntax der Lambda-Ausdrücke diskutiert, ob man den Underscore - ähnlich wie in Scala oder anderen Sprachen - auch in Java als Platzhalter für einen Namen zulassen sollte. Die Expertengruppe hatte sich dagegen entschieden und es gibt von Brian Goetz, Oracle's Java Language Architect, die etwas nebulöse Aussage, man habe den Underscore für mögliche sinnvolle Dinge in Java 10+ vorgesehen (siehe / SOVF /). Effectively-final Variablen in try-with-resourcesDas try-with-resourcesKonstrukt wurde mit Java 7 eingeführt. Es sieht so aus:String readFirstLineFromFile(String path) throws IOException { try ( BufferedReader br = new BufferedReader(new FileReader(path)) ) { return br.readLine(); }
}
Vor dem eigentlich
try
-Block
darf ein Objekt von einem Typ erzeugt werden, der das
AutoCloseable
Interface implementiert. Der Compiler generiert dann automatisch einen
finally
-Block,
in dem die
close()
-Methode aufgerufen
wird. Für dieses Konstrukt musste in Java 7 stets eine neue Variable
deklariert werden, die der Compiler dann implizit als
final
deklariert hat und die dann nur im try-with-resources-Konstrukt verwendbar
war.
In Java 9 darf man jetzt das
AutoCloseable
-Objekt
vor dem try-with-resources-Konstrukt erzeugen und im try-with-resources-Konstrukt
eine Variable auf das Objekt verwenden. Diese Variable muss nicht explizit
als
final
deklariert sein, aber der Compiler
behandelt sie wie eine
final
-Variable.
Das heißt die Variable ist „effectively final“. Es darf also in
Java 9 so aussehen:
String readFirstLineFromFile(String path) throws IOException { final BufferedReader br = new BufferedReader(new FileReader(path)); try (br) { return br.readLine(); }
}
wobei man die final -Deklaration auch weglassen darf. Ergänzungen zum Stream APIInterface Stream
Das Interface
Stream
im Package
java.util.stream
ist mit Java
8 zum JDK hinzugekommen und definiert die sogenannten Stream-Operationen.
Für Java 9 ist es ergänzt worden: es gibt zwei neue Stream-Operationen
takeWhile()
und
dropWhile()
, eine Variante der Factory-Methode
iterate()
mit Endekriterium und eine neue Factory-Methode
ofNullable()
.
Die Operationen
takeWhile()
und
dropWhile()
ähneln
den Operationen
limit()
und
skip()
.
Während man bei
limit()
und
skip()
angibt,
wie viele Elemente verarbeitet bzw. übersprungen werden sollen, wird bei
takeWhile()
und
dropWhile()
eine Bedingung mitgegeben,
die bestimmt, wie lange Stream-Elemente verarbeitet bzw. übersprungen
werden.
Hier ein Beispiel:
IntStream.range(0,100) . limit(5) // verarbeite nur die ersten 5 Stream-Elemente
.forEach(System.out::print
);
IntStream.range(0,100) . takeWhile( i->i<5 ) // verarbeite die ersten Stream-Elemente , solange sie < 5 sind
.forEach(System.out::print
);
In beiden Fällen werden die Zahlen
0
1 2 3 4
ausgegeben.
Die Factory-Methode
iterate()
gibt es schon seit Java 8. Sie erzeugt einen unendlich langen Stream.
In Java 9 gibt es nun eine zweite Variante davon, bei der man eine Abbruchbedingung
spezifizieren kann.
Hier ein Beispiel:
Stream. iterate(0 , i->i+1) // erzeuge die Zahlen 0 1 2 3 4 5 usw. ohne Ende .limit(5 ) .forEach(System.out::print();
Stream. iterate(0 , i ->i< 5 , i->i+1) // erzeuge die Zahlen 0 1 2 3 4 5 usw., solange sie < 5 sind
.forEach(System.out::print();
In beiden Fällen werden die Zahlen
0
1 2 3 4
ausgegeben.
Die Factory-Methode
ofNullable()
ist eine reine Convenience-Methode. Sie erzeugt einen leeren Stream, wenn
das Argument
null
ist und sonst einen
Stream mit einem Element.
Anstelle von (arg==null)?Stream.empty():Stream.of(arg) kann man jetzt sagen Stream.ofNullable(arg)
Diese kompaktere Schreibweise verbessert im Zusammenhang mit der Stream-Operation flatMap() die Lesbarkeit. Klasse Collectors
In der Klasse
Collectors
gibt es neue Downstream-Kollektoren für die Nachverarbeitung der Gruppen
nach einem Grouping oder Partitioning, nämlich
Collectors.filtering()
und
Collectors.flatMapping()
.
Klasse Optional
Die
Optional
-Klassen
haben ebenfalls einige neue Methoden bekommen:
-
stream()
macht aus einem
Optional<T>
einen
Stream<T>
.
-
or()
ersetzt ein leeres
Optional
durch ein
anderes
Optional
.
-
ifPresentOrElse()
akzeptiert Aktionen sowohl für das nicht-leere als auch das leere Optional;
bisher gab es nur
ifPresent()
.
Eine Reihe von JDK-Abstraktionen liefern
in Java 9 ihre Ergebnisse als Streams und haben entsprechende neue Methoden
bekommen, z.B. die Methode
date
s
Until()
in der Klasse
java.time.LocalDate
, die
Methoden
tokens()
und
findAll()
in der Klasse
java.util.Scanner
oder
die Methode
results()
in der Klasse
java.util.regex.Matcher
.
Methode Files.lines()
Die statische Methode
Stream
<String>
lin
es(Path path, Charset cs)
in der
Klasse
java.nio.file.Files
ist optimiert
worden. Sie liefert die Zeilen einer Datei als
Stream<String>
.
Neu ist in Java 9, dass sie Memory-Mapped-I/O verwendet, um im parallelen
Falle das Splitting zu beschleunigen - zumindest für die Character-Encodings
UTF-8,
US-ASCII
and
ISO-8859-1
.
Ergänzungen zum Process API
Die Klasse
java.lang.Process
gibt es schon seit Java 1.0. Man kann damit Prozesse auf Betriebsystemebene
starten. Mit Java 5 ist ein
ProcessBuilder
hinzugekommen, der in Java 7 erweitert wurde und insbesondere das Umlenken
der Input- und Output-Kanäle solcher Prozesse vereinfacht.
Mit Java 9 kommt jetzt eine neue Abstraktion
ProcessHandle
dazu, welche Zugriff und Informationen über beliebige Prozesse liefert.
Man kann sich beispielsweise eine Liste aller Prozesse auf der Maschine
geben lassen und sich Information dazu holen. Das ist anders als bei
der Klasse
Process
, die nur Prozesse
beschreibt, die im aktuellen Prozess der JVM gestartet wurden. Anders
als beim
Process
bekommt man aber beim
ProcessHandle
keinen Zugriff auf die Input- und Output-Kanäle des Prozesses.
Auch die Klasse
Process
selbst wurde in Java 9 erweitert: sie liefert jetzt mehr Informationen
über den Prozess, wie z.B. die Liste der Child- oder Parent-Prozesse,
die Liveness und allgemeine Information wie Prozess-Id, Startzeit, CPU-Zeit,
Kommandozeile, etc.
Schon in Java 8 wurde die Klasse
Process
u. a. mit einer
waitFor()-
Methode ausgestattet,
die es erlaubt, auf die Terminierung eines Prozesses zu warten. In Java
9 gibt es nun als Alternative zu dieser
waitFor()
-Methode
eine
onExit()
-Methode, die ein
CompletableFuture<Process>
liefert. Damit muss man nicht mehr synchron auf die Beendigung der Prozesse
warten, sondern kann die asynchronen Möglichkeiten des
CompletableFuture
nutzen.
Als Beispiel wollen wir mehrere Prozess-Pipelines
starten und warten, bis alle Prozesse in allen Pipelines fertig sind, damit
wir anschließend die Ergebnisdatei verarbeiten können, in die alle Prozesse
geschrieben haben. Das hätte man in Java 8 mit Hilfe von der
waitFor()-
Methode
so machen können:
List<Process> p1 =
pdfFileNamesIn(
dirName1
);
p1.addAll(p2);
p1.addAll(p3);
//1
}
use
ResultFile
();
//3
Wir haben alle Prozesse in eine Liste kopiert
(//1), dann synchron auf das Ende eines jeden Prozesses gewartet (//2)
und schließlich die Ergebnisdatei verarbeitet (//3).
In Java 9 geht es jetzt asynchron mit Hilfe
der
onExit()-
Methode:
List<Process> p1 =
pdfFileNamesIn(dirName1);
CompletableFuture
<?>[]
processFutures
CompletableFuture.
allOf
(
processFutures)
//
4
Wenn alle Prozesse fertig sind (//4), dann
soll asynchron in einem Worker-Thread die Ergebnisdatei verarbeitet werden
(//5). Auf die Terminierung aller Threads kann man mit Hilfe der Methode
CompletableFuture.allOf()
reagieren. Dafür brauchen wir ein Array mit allen Futures für alle
Prozesse. Dieses Array erzeugen wir mit Hilfe eines Streams: wir vereinen
alle Prozesse zu einem
Stream<Process>
(//1), holen zu jedem Prozess ein
CompletableFuture<Process>
für das Ende des Prozesses (//2) und legen alle
CompletableFuture<Process>
in einem Array ab (//3), das wir anschießend an die Methode
CompletableFuture.allOf()
übergeben können.
|
|||||||||||||||||||
© Copyright 1995-2018 by Angelika Langer. All Rights Reserved. URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/91.Java9.What-is-new-in-Java-9/90.java-9.1.overview.ready_3.html> last update: 26 Oct 2018 |