Java 11 hat keine innovativen Funktionen eingeführt, enthält jedoch einige Juwelen, von denen Sie vielleicht noch nichts gehört haben. Sie haben sich bereits die neuesten String
, Optional
, Collection
und anderen Arbeitspferden angesehen? Wenn nicht, dann sind Sie bei der Adresse angelangt: Heute werden wir uns 11 versteckte Juwelen aus Java 11 ansehen!
Typinferenz für Lambda-Parameter
Beim Schreiben eines Lambda-Ausdrucks können Sie wählen, ob Sie Typen explizit angeben oder überspringen möchten:
Function<String, String> append = string -> string + " "; Function<String, String> append = (String s) -> s + " ";
Java 10 führte var
, konnte aber nicht in Lambdas verwendet werden:
In Java 11 ist dies bereits möglich. Aber warum? Es scheint nicht so, als hätte var
mehr als nur einen Typpass gegeben. Obwohl dies der Fall ist, hat die Verwendung von var
zwei geringfügige Vorteile:
- macht die Verwendung von
var
universeller, indem die Ausnahme von der Regel entfernt wird - Mit dieser Option können Sie dem Parametertyp Anmerkungen hinzufügen, ohne den vollständigen Namen verwenden zu müssen
Hier ist ein Beispiel für den zweiten Fall:
List<EnterpriseGradeType<With, Generics>> types = ; types .stream()
Obwohl das Mischen abgeleiteter, expliziter und impliziter Typen in Lambda-Ausdrücken der Form (var type, String option, index) -> ...
implementiert werden kann, wurde diese Arbeit ( im Rahmen von JEP-323 ) nicht ausgeführt. Daher ist es notwendig, einen der drei Ansätze zu wählen und ihn für alle Parameter des Lambda-Ausdrucks einzuhalten. Die Notwendigkeit, var
für alle Parameter anzugeben, um Anmerkungen für einen von ihnen hinzuzufügen, kann etwas ärgerlich sein, ist aber im Allgemeinen tolerierbar.
Stream-Verarbeitung von Strings mit 'String::lines'
Hast du eine mehrzeilige Zeichenfolge? Möchten Sie mit jeder Zeile etwas anfangen? Dann ist String::lines
die richtige Wahl:
var multiline = "\r\n\r\n\r\n"; multiline .lines()
Beachten Sie, dass die ursprüngliche Zeile die \r\n
Schraubenbegrenzer verwendet und obwohl ich unter Linux bin, hat lines()
immer noch beschädigt. Dies liegt an der Tatsache, dass diese Methode trotz des aktuellen Betriebssystems \r
, \n
und \r\n
als Zeilenumbrüche interpretiert - auch wenn sie in derselben Zeile gemischt sind.
Ein Zeilenstrom enthält niemals die Zeilentrennzeichen selbst. Zeilen können leer sein ( "\n\n \n\n"
, der 5 Zeilen enthält), aber die letzte Zeile der ursprünglichen Zeile wird ignoriert, wenn sie leer ist ( "\n\n"
; 2 Zeilen). (Hinweis des Übersetzers: Es ist praktisch, wenn sie eine line
, aber eine string
, und wir haben beide.)
Im Gegensatz zu split("\R")
sind lines()
faul und bieten, wie ich zitiere , "eine bessere Leistung [...] durch schnellere Suche nach neuen Zeilenumbrüchen". (Wenn jemand einen Benchmark für JMH zur Überprüfung einreichen möchte, lassen Sie es mich wissen.) Es spiegelt auch den Verarbeitungsalgorithmus besser wider und verwendet eine bequemere Datenstruktur (Stream anstelle von Array).
Leerzeichen mit 'String::strip'
usw. entfernen
Anfangs hatte String
eine trim
zum Entfernen von Leerzeichen, die als alles mit Codes bis U+0020
. Ja, BACKSPACE
( U+0008)
ist ein Leerraum wie BELL
( U+0007
), aber LINE SEPARATOR
( U+2028
) wird nicht mehr als solcher betrachtet.
Java 11 führte die strip
Methode ein, deren Ansatz mehr Nuancen aufweist. Es verwendet die Character::isWhitespace
von Java 5, um zu bestimmen, was genau entfernt werden muss. Aus seiner Dokumentation geht hervor, dass dies:
SPACE SEPARATOR
, LINE SEPARATOR
, PARAGRAPH SEPARATOR
, aber kein untrennbarer RaumHORIZONTAL TABULATION
( U+0009
), LINE FEED
( U+000A
), VERTICAL TABULATION
( U+000B
), FORM FEED
VERTICAL TABULATION
( U+000B
), CARRIAGE RETURN
( U+000D
)FILE SEPARATOR
( U+001C
), GROUP SEPARATOR
( U+001D
), RECORD SEPARATOR
( U+001E
), UNIT SEPARATOR
( U+001F
)
Mit der gleichen Logik gibt es zwei weitere Reinigungsmethoden, stripLeading
und stripTailing
, die genau das tun, was von ihnen erwartet wird.
Und wenn Sie nur herausfinden müssen, ob die Zeile nach dem Entfernen von Leerzeichen leer ist, müssen Sie sie nicht wirklich löschen - verwenden Sie einfach isBlank
:
" ".isBlank();
Wiederholen von Zeichenfolgen mit 'String::repeat'
Fangen Sie die Idee:
Schritt 1: JDK im Auge behalten

Schritt 2: Finden von Fragen zu StackOverflow

Schritt 3: Ankunft mit einer neuen Antwort basierend auf zukünftigen Änderungen

Schritt 4: ????
Schritt 4: Gewinn

Wie Sie sich vorstellen können, hat String
eine neue repeat(int)
. Es funktioniert genau im Einklang mit den Erwartungen und es gibt wenig zu diskutieren.
Erstellen von Pfaden mit 'Path::of'
Ich mag die Path
API sehr, aber das Konvertieren von Pfaden zwischen verschiedenen Ansichten (wie Path
, File
, URL
, URI
und String
) ist immer noch ärgerlich. Dieser Punkt ist in Java 11 weniger verwirrend geworden, indem zwei Paths::get
Methoden in Path::of
Methoden kopiert wurden:
Path tmp = Path.of("/home/nipa", "tmp"); Path codefx = Path.of(URI.create("http://codefx.org"));
Sie können als kanonisch betrachtet werden, da beide alten Paths::get
Methoden neue Optionen verwenden.
Lesen und Schreiben von Dateien mit 'Files::readString'
und 'Files::writeString'
Wenn ich aus einer großen Datei lesen muss, verwende ich normalerweise Files::lines
, um einen faulen Stream ihrer Zeilen zu erhalten. Um eine große Datenmenge zu schreiben, die möglicherweise nicht vollständig im Speicher gespeichert ist, verwende ich Files::write
um sie als Iterable<String>
.
Aber was ist mit dem einfachen Fall, wenn ich den Inhalt einer Datei als einzelne Zeile verarbeiten möchte? Dies ist nicht sehr praktisch, da Files::readAllBytes
und die entsprechenden Varianten von Files::write
mit Byte-Arrays arbeiten.
Und dann erscheint Java 11 und writeString
den Files
readString
und writeString
:
String haiku = Files.readString(Path.of("haiku.txt")); String modified = modify(haiku); Files.writeString(Path.of("haiku-mod.txt"), modified);
Klar und einfach zu bedienen. Bei Bedarf können Sie den Charset
an readString
und in writeString
auch an ein OpenOptions
Array übergeben.
Leeren Sie die E / A mit 'Reader::nullReader'
usw.
OutputStream
Sie einen OutputStream
, der nirgendwo schreibt? Oder ein leerer InputStream
? Was ist mit Reader
und Writer
, die nichts tun? Java 11 bietet alles:
InputStream input = InputStream.nullInputStream(); OutputStream output = OutputStream.nullOutputStream(); Reader reader = Reader.nullReader(); Writer writer = Writer.nullWriter();
(Anmerkung des Übersetzers: In commons-io
diese Klassen seit ungefähr 2014.)
Ich bin jedoch überrascht - ist null
wirklich das beste Präfix? Mir gefällt nicht, wie es "absichtliche Abwesenheit" bedeutet ... Vielleicht wäre es besser, noOp
zu verwenden? (Anmerkung des Übersetzers: Höchstwahrscheinlich wurde dieses Präfix aufgrund der allgemeinen Verwendung von /dev/null
.)
{ } ~> [ ]
mit 'Collection::toArray'
Wie konvertieren Sie Sammlungen in Arrays?
Die erste Option, objects
, verliert alle Informationen zu Typen, sodass sie im Flug ist. Was ist mit dem Rest? Beide sind sperrig, aber der erste ist kürzer. Letzteres erstellt ein Array mit der erforderlichen Größe, damit es produktiver aussieht (dh es scheint "produktiver zu sein", siehe Glaubwürdigkeit ). Aber ist es wirklich produktiver? Nein, im Gegenteil, es ist (im Moment) langsamer .
Aber warum sollte mich das interessieren? Gibt es nicht einen besseren Weg, dies zu tun? In Java 11 gibt es:
String[] strings_fun = list.toArray(String[]::new);
Eine neue Variante von Collection::toArray
, die IntFunction<T[]>
akzeptiert, d. H. Eine Funktion, die die Größe eines Arrays abruft und ein Array mit der erforderlichen Größe zurückgibt Es kann kurz als Referenz auf einen Konstruktor der Form T[]::new
(für ein bekanntes T
) ausgedrückt werden.
Interessanterweise Collection#toArray(IntFunction<T[]>)
die Standardimplementierung von Collection#toArray(IntFunction<T[]>)
immer 0
an den Array-Generator. Zuerst habe ich beschlossen, dass diese Lösung auf der besten Leistung für Arrays mit einer Länge von Null basiert. Jetzt denke ich, dass der Grund dafür sein kann, dass die Berechnung der Größe für einige Sammlungen eine sehr teure Operation sein kann und Sie diesen Ansatz nicht in der Standardimplementierung von Collection
. Bestimmte Sammlungsimplementierungen wie ArrayList
können diesen Ansatz jedoch ändern, in Java 11 jedoch nicht. Das ist es nicht wert, denke ich.
Abwesenheitsprüfung mit 'Optional::isEmpty'
Bei der häufigen Verwendung von Optional
, insbesondere in großen Projekten, bei denen Sie häufig auf einen nicht Optional
Ansatz stoßen, müssen Sie häufig prüfen, ob dieser einen Wert hat. Optional::isPresent
gibt es eine Optional::isPresent
Methode. Aber genauso oft muss man das Gegenteil wissen - dass Optional
leer ist. Kein Problem, benutze einfach !opt.isPresent()
, oder?
Natürlich ist es so möglich, aber es ist fast immer einfacher, die if
Logik zu verstehen, if
ihre Bedingung nicht invertiert ist. Und manchmal erscheint Optional
am Ende einer langen Kette von Anrufen, und wenn Sie es auf nichts überprüfen müssen, müssen Sie wetten !
ganz am Anfang:
public boolean needsToCompleteAddress(User user) { return !getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isPresent(); }
In diesem Fall überspringen Sie es !
sehr einfach. Ab Java 11 gibt es eine bessere Option:
public boolean needsToCompleteAddress(User user) { return getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isEmpty(); }
Prädikate mit 'Predicate::not'
invertieren
Apropos Invertieren ... Die Predicate
verfügt über eine negate
: Sie gibt ein neues Prädikat zurück, das dieselbe Prüfung durchführt, aber das Ergebnis invertiert. Leider schaffe ich es selten, es zu benutzen ...
Das Problem ist, dass ich selten Zugriff auf die Predicate
Instanz habe. Häufiger möchte ich eine solche Instanz über einen Link zu einer Methode abrufen (und invertieren), aber damit dies funktioniert, muss der Compiler wissen, wohin er den Verweis auf die Methode bringen soll - ohne sie kann er nichts tun. Und genau das passiert, wenn Sie das (String::isBlank).negate()
: Der Compiler weiß nicht mehr, was String::isBlank
sein soll, und gibt auf. Eine korrekt angegebene Kaste behebt dies, aber zu welchem Preis?
Obwohl es eine einfache Lösung gibt. Verwenden Sie nicht die negate
instance, sondern die neue statische Methode Predicate.not(Predicate<T>)
aus Java 11:
Stream .of("a", "b", "", "c")
Schon besser!
Reguläre Ausdrücke als Prädikat mit 'Pattern::asMatchPredicate'
Gibt es einen regulären Ausdruck? Müssen Sie Daten darauf filtern? Wie wäre es damit:
Pattern nonWordCharacter = Pattern.compile("\\W"); Stream .of("Metallica", "Motörhead") .filter(nonWordCharacter.asPredicate()) .forEach(System.out::println);
Ich habe mich sehr gefreut, diese Methode zu finden! Es ist erwähnenswert, dass dies eine Methode aus Java 8 ist. Hoppla, ich habe sie dann verpasst. Java 11 hat eine weitere ähnliche Methode hinzugefügt: Pattern::asMatchPredicate
. Was ist der Unterschied?
asPredicate
prüft, ob die Zeichenfolge oder ein Teil der Zeichenfolge mit dem Muster übereinstimmt (funktioniert wie s -> this.matcher(s).find()
)asMatchPredicate
prüft, ob die gesamte Zeichenfolge mit dem Muster übereinstimmt (funktioniert wie s -> this.matcher(s).matches()
)
Zum Beispiel haben wir einen regulären Ausdruck, der Telefonnummern überprüft, aber keine ^
und $
, um den Anfang und das Ende einer Zeile zu verfolgen. Dann funktioniert der folgende Code nicht wie erwartet:
prospectivePhoneNumbers .stream() .filter(phoneNumberPatter.asPredicate()) .forEach(this::robocall);
Hast du einen Fehler bemerkt? Eine Zeile wie " -152 ? +1-202-456-1414"
wird gefiltert, da sie eine gültige Telefonnummer enthält. Auf der anderen Seite lässt Pattern::asMatchPredicate
nicht zu, da die gesamte Zeichenfolge nicht mehr mit dem Muster übereinstimmt.
Selbsttest
Hier ist eine Übersicht aller elf Perlen - erinnern Sie sich noch daran, was jede Methode bewirkt? Wenn ja, haben Sie den Test bestanden.
- in
String
:
Stream<String> lines()
String strip()
String stripLeading()
String stripTrailing()
boolean isBlank()
String repeat(int)
- im
Path
:
static Path of(String, String...)
static Path of(URI)
- in
Files
:
String readString(Path) throws IOException
Path writeString(Path, CharSequence, OpenOption...) throws IOException
Path writeString(Path, CharSequence, Charset, OpenOption...) throws IOException
- in
InputStream
: static InputStream nullInputStream()
- in
OutputStream
: static OutputStream nullOutputStream()
- im
Reader
: static Reader nullReader()
- in
Writer
: static Writer nullWriter()
- in
Collection
: T[] toArray(IntFunction<T[]>)
- in
Optional
: boolean isEmpty()
- im
Predicate
: static Predicate<T> not(Predicate<T>)
- im
Pattern
: Predicate<String> asMatchPredicate()
Viel Spaß mit Java 11!