Neue Funktionen in Java 8

1. Übersicht

In diesem Artikel werfen wir einen kurzen Blick auf einige der interessantesten neuen Funktionen in Java 8.

Wir werden über Folgendes sprechen: Standard- und statische Schnittstellenmethoden, Methodenreferenz und Optional.

Wir haben bereits einige Funktionen der Java 8-Version behandelt - Stream-API, Lambda-Ausdrücke und funktionale Schnittstellen -, da es sich um umfassende Themen handelt, die einen gesonderten Blick verdienen.

2. Schnittstellenstandard und statische Methoden

Vor Java 8 konnten Schnittstellen nur öffentliche abstrakte Methoden haben. Es war nicht möglich, der vorhandenen Schnittstelle neue Funktionen hinzuzufügen, ohne alle implementierenden Klassen zu zwingen, eine Implementierung der neuen Methoden zu erstellen, und es war auch nicht möglich, Schnittstellenmethoden mit einer Implementierung zu erstellen.

Ab Java 8 können Schnittstellen statische und Standardmethoden haben , die, obwohl sie in einer Schnittstelle deklariert sind, ein definiertes Verhalten aufweisen.

2.1. Statische Methode

Betrachten Sie die folgende Methode der Schnittstelle (nennen wir diese Schnittstelle Fahrzeug ):

static String producer() { return "N&F Vehicles"; }

Die statische Producer () -Methode ist nur über und innerhalb einer Schnittstelle verfügbar. Es kann nicht von einer implementierenden Klasse überschrieben werden.

Um es außerhalb der Schnittstelle aufzurufen, sollte der Standardansatz für den Aufruf statischer Methoden verwendet werden:

String producer = Vehicle.producer();

2.2. Standardmethode

Standardmethoden werden mit dem neuen Standardschlüsselwort deklariert . Diese sind über die Instanz der implementierenden Klasse zugänglich und können überschrieben werden.

Fügen wir unserer Fahrzeugschnittstelle eine Standardmethode hinzu , die auch die statische Methode dieser Schnittstelle aufruft:

default String getOverview() { return "ATV made by " + producer(); }

Angenommen, diese Schnittstelle wird von der Klasse VehicleImpl implementiert. Zum Ausführen der Standardmethode sollte eine Instanz dieser Klasse erstellt werden:

Vehicle vehicle = new VehicleImpl(); String overview = vehicle.getOverview();

3. Methodenreferenzen

Die Methodenreferenz kann als kürzere und besser lesbare Alternative für einen Lambda-Ausdruck verwendet werden, der nur eine vorhandene Methode aufruft. Es gibt vier Varianten von Methodenreferenzen.

3.1. Verweis auf eine statische Methode

Der Verweis auf eine statische Methode enthält die folgende Syntax: ContainingClass :: methodName.

Versuchen wir, alle leeren Zeichenfolgen in der Liste mithilfe der Stream-API zu zählen.

boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));

Schauen Sie sich den Lambda-Ausdruck in der anyMatch () -Methode genauer an. Er ruft lediglich eine statische Methode isRealUser (User user) der User- Klasse auf. Es kann also durch einen Verweis auf eine statische Methode ersetzt werden:

boolean isReal = list.stream().anyMatch(User::isRealUser);

Diese Art von Code sieht viel informativer aus.

3.2. Verweis auf eine Instanzmethode

Der Verweis auf eine Instanzmethode enthält die folgende Syntax: c ontainingInstance :: methodName. Die folgende Codeaufrufmethode isLegalName (String string) vom Typ User überprüft einen Eingabeparameter:

User user = new User(); boolean isLegalName = list.stream().anyMatch(user::isLegalName); 

3.3. Verweis auf eine Instanzmethode eines Objekts eines bestimmten Typs

Diese Referenzmethode verwendet die folgende Syntax: C ontainingType :: methodName. Ein Beispiel::

long count = list.stream().filter(String::isEmpty).count();

3.4. Verweis auf einen Konstruktor

Ein Verweis auf einen Konstruktor hat die folgende Syntax: ClassName :: new. Da der Konstruktor in Java eine spezielle Methode ist, kann mit Hilfe von new als Methodenname auch eine Methodenreferenz darauf angewendet werden .

Stream stream = list.stream().map(User::new);

4. Optional

Bevor Java 8-Entwickler die von ihnen genannten Werte sorgfältig validieren mussten, bestand die Möglichkeit, die NullPointerException (NPE) auszulösen . All diese Überprüfungen erforderten einen ziemlich nervigen und fehleranfälligen Boilerplate-Code.

Java 8 Die optionale Klasse kann helfen, Situationen zu bewältigen, in denen die Möglichkeit besteht, die NPE zu erhalten . Es funktioniert als Container für das Objekt vom Typ T. Es kann einen Wert dieses Objekts zurückgeben, wenn dieser Wert keine Null ist . Wenn der Wert in diesem Container null ist , können einige vordefinierte Aktionen ausgeführt werden, anstatt NPE auszulösen.

4.1. Erstellung des optionalen

Eine Instanz der optionalen Klasse kann mithilfe ihrer statischen Methoden erstellt werden:

Optional optional = Optional.empty();

Gibt eine leere Option zurück.

String str = "value"; Optional optional = Optional.of(str);

Gibt eine Option zurück, die einen Wert ungleich Null enthält.

Optional optional = Optional.ofNullable(getString());

Will return an Optional with a specific value or an empty Optional if the parameter is null.

4.2. Optional Usage

For example, you expect to get a List and in the case of null you want to substitute it with a new instance of an ArrayList. With pre-Java 8's code you need to do something like this:

List list = getList(); List listOpt = list != null ? list : new ArrayList();

With Java 8 the same functionality can be achieved with a much shorter code:

List listOpt = getList().orElseGet(() -> new ArrayList());

There is even more boilerplate code when you need to reach some object's field in the old way. Assume you have an object of type User which has a field of type Address with a field street of type String. And for some reason you need to return a value of the street field if some exist or a default value if street is null:

User user = getUser(); if (user != null) { Address address = user.getAddress(); if (address != null) { String street = address.getStreet(); if (street != null) { return street; } } } return "not specified";

This can be simplified with Optional:

Optional user = Optional.ofNullable(getUser()); String result = user .map(User::getAddress) .map(Address::getStreet) .orElse("not specified");

In this example we used the map() method to convert results of calling the getAdress() to the Optional and getStreet() to Optional. If any of these methods returned null the map() method would return an empty Optional.

Imagine that our getters return Optional. So, we should use the flatMap() method instead of the map():

Optional optionalUser = Optional.ofNullable(getOptionalUser()); String result = optionalUser .flatMap(OptionalUser::getAddress) .flatMap(OptionalAddress::getStreet) .orElse("not specified");

Another use case of Optional is changing NPE with another exception. So, as we did previously, let's try to do this in pre-Java 8's style:

String value = null; String result = ""; try { result = value.toUpperCase(); } catch (NullPointerException exception) { throw new CustomException(); }

And what if we use Optional? The answer is more readable and simpler:

String value = null; Optional valueOpt = Optional.ofNullable(value); String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();

Notice, that how and for what purpose to use Optional in your app is a serious and controversial design decision, and explanation of its all pros and cons is out of the scope of this article. If you are interested, you can dig deeper, there are plenty of interesting articles on the Internet devoted to this problem. This one and this another one could be very helpful.

5. Conclusion

In this article, we are briefly discussing some interesting new features in Java 8.

There are of course many other additions and improvements which are spread across many Java 8 JDK packages and classes.

But, the information illustrated in this article is a good starting point for exploring and learning about some of these new features.

Schließlich ist der gesamte Quellcode für den Artikel auf GitHub verfügbar.