Funcții noi în Java 8

1. Prezentare generală

În acest articol, vom analiza rapid unele dintre cele mai interesante caracteristici noi din Java 8.

Vom vorbi despre: implicit interfață și metode statice, referință metodă și opțional.

Am acoperit deja câteva caracteristici ale versiunii Java 8 - API de flux, expresii lambda și interfețe funcționale - deoarece sunt subiecte cuprinzătoare care merită un aspect separat.

2. Interfață implicită și metode statice

Înainte de Java 8, interfețele puteau avea doar metode abstracte publice. Nu a fost posibil să adăugați funcționalități noi la interfața existentă fără a forța toate clasele de implementare să creeze o implementare a noilor metode și nici nu a fost posibil să creați metode de interfață cu o implementare.

Începând cu Java 8, interfețele pot avea metode statice și implicite care, deși sunt declarate într-o interfață, au un comportament definit.

2.1. Metoda statică

Luați în considerare următoarea metodă a interfeței (să numim această interfață Vehicul ):

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

Metoda static producer () este disponibilă numai prin intermediul și în interiorul unei interfețe. Nu poate fi anulat de o clasă de implementare.

Pentru a o apela în afara interfeței, ar trebui utilizată abordarea standard pentru apelul cu metodă statică:

String producer = Vehicle.producer();

2.2. Metoda implicită

Metodele implicite sunt declarate folosind noul cuvânt cheie implicit . Acestea sunt accesibile prin instanța clasei de implementare și pot fi anulate.

Să adăugăm o metodă implicită la interfața vehiculului nostru , care va efectua și un apel către metoda statică a acestei interfețe:

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

Să presupunem că această interfață este implementată de clasa VehicleImpl. Pentru executarea metodei implicite ar trebui creată o instanță din această clasă:

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

3. Referințe la metodă

Referința la metodă poate fi utilizată ca o alternativă mai scurtă și mai lizibilă pentru o expresie lambda care apelează doar o metodă existentă. Există patru variante de referințe la metodă.

3.1. Trimitere la o metodă statică

Referința la o metodă statică deține următoarea sintaxă: ContainingClass :: methodName.

Să încercăm să numărăm toate șirurile goale din Listă cu ajutorul API-ului Stream.

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

Aruncați o privire mai atentă la expresia lambda din metoda anyMatch () , doar că face un apel către o metodă statică isRealUser (User user) din clasa User . Deci, poate fi substituit cu o referință la o metodă statică:

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

Acest tip de cod arată mult mai informativ.

3.2. Trimitere la o metodă de instanță

Referința la o metodă de instanță conține următoarea sintaxă: c ontainingInstance :: methodName. Următoarea metodă de apelare cod esteLegalName (șir de șiruri) de tip User care validează un parametru de intrare:

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

3.3. Referință la o metodă de instanță a unui obiect de un anumit tip

Această metodă de referință are următoarea sintaxă: C ontainingType :: methodName. Un exemplu::

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

3.4. Reference to a Constructor

A reference to a constructor takes the following syntax: ClassName::new. As constructor in Java is a special method, method reference could be applied to it too with the help of newas a method name.

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

4. Optional

Before Java 8 developers had to carefully validate values they referred to, because of a possibility of throwing the NullPointerException (NPE). All these checks demanded a pretty annoying and error-prone boilerplate code.

Java 8 Clasa opțională vă poate ajuta să gestionați situațiile în care există posibilitatea de a obține NPE . Funcționează ca un container pentru obiectul de tip T. Poate returna o valoare a acestui obiect dacă această valoare nu este nulă . Când valoarea din interiorul acestui container este nulă , permite efectuarea unor acțiuni predefinite în loc să arunce NPE.

4.1. Crearea opționalului

O instanță a clasei Opționale poate fi creată cu ajutorul metodelor sale statice:

Optional optional = Optional.empty();

Returnează un Opțional gol .

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

Returnează o opțională care conține o valoare non-nulă.

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.

În cele din urmă, tot codul sursă pentru articol este disponibil pe GitHub.