Nouvelles fonctionnalités de Java 8

1. Vue d'ensemble

Dans cet article, nous examinerons rapidement certaines des nouvelles fonctionnalités les plus intéressantes de Java 8.

Nous parlerons de: méthodes par défaut et statiques de l'interface, référence de méthode et facultatif.

Nous avons déjà couvert certaines des fonctionnalités de la version de Java 8 - API de flux, expressions lambda et interfaces fonctionnelles - car ce sont des sujets complets qui méritent un examen séparé.

2. Méthodes par défaut et statiques de l'interface

Avant Java 8, les interfaces ne pouvaient avoir que des méthodes abstraites publiques. Il n'était pas possible d'ajouter de nouvelles fonctionnalités à l'interface existante sans forcer toutes les classes d'implémentation à créer une implémentation des nouvelles méthodes, ni de créer des méthodes d'interface avec une implémentation.

À partir de Java 8, les interfaces peuvent avoir des méthodes statiques et par défaut qui, bien qu'elles soient déclarées dans une interface, ont un comportement défini.

2.1. Méthode statique

Considérez la méthode suivante de l'interface (appelons cette interface Vehicle ):

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

La méthode static Producer () est disponible uniquement via et à l'intérieur d'une interface. Il ne peut pas être remplacé par une classe d'implémentation.

Pour l'appeler en dehors de l'interface, l'approche standard pour l'appel de méthode statique doit être utilisée:

String producer = Vehicle.producer();

2.2. Méthode par défaut

Les méthodes par défaut sont déclarées à l'aide du nouveau mot-clé par défaut . Ceux-ci sont accessibles via l'instance de la classe d'implémentation et peuvent être remplacés.

Ajoutons une méthode par défaut à notre interface Véhicule , qui fera également un appel à la méthode statique de cette interface:

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

Supposons que cette interface est implémentée par la classe VehicleImpl. Pour exécuter la méthode par défaut , une instance de cette classe doit être créée:

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

3. Références de méthode

La référence de méthode peut être utilisée comme une alternative plus courte et plus lisible pour une expression lambda qui appelle uniquement une méthode existante. Il existe quatre variantes de références de méthode.

3.1. Référence à une méthode statique

La référence à une méthode statique contient la syntaxe suivante: ContainingClass :: methodName.

Essayons de compter toutes les chaînes vides dans la liste à l'aide de l'API Stream.

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

Examinez de plus près l'expression lambda dans la méthode anyMatch () , elle appelle simplement une méthode statique isRealUser (User user) de la classe User . Il peut donc être remplacé par une référence à une méthode statique:

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

Ce type de code semble beaucoup plus informatif.

3.2. Référence à une méthode d'instance

La référence à une méthode d'instance contient la syntaxe suivante: c ontainingInstance :: methodName. Le code suivant appelle la méthode isLegalName (String string) de type User qui valide un paramètre d'entrée:

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

3.3. Référence à une méthode d'instance d'un objet d'un type particulier

Cette méthode de référence utilise la syntaxe suivante: C ontainingType :: methodName. Un exemple::

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

3.4. Référence à un constructeur

Une référence à un constructeur prend la syntaxe suivante: ClassName :: new. Comme le constructeur en Java est une méthode spéciale, la référence de méthode peut également lui être appliquée à l'aide de new comme nom de méthode .

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

4. Facultatif

Avant Java 8, les développeurs devaient valider soigneusement les valeurs auxquelles ils faisaient référence, en raison de la possibilité de lancer l' exception NullPointerException (NPE) . Tous ces contrôles exigeaient un code passe-partout assez ennuyeux et sujet aux erreurs.

La classe facultative Java 8 peut aider à gérer les situations dans lesquelles il est possible d'obtenir le NPE . Il fonctionne comme un conteneur pour l'objet de type T. Il peut renvoyer une valeur de cet objet si cette valeur n'est pas nulle . Lorsque la valeur à l'intérieur de ce conteneur est nulle, cela permet d'effectuer certaines actions prédéfinies au lieu de lancer NPE.

4.1. Création de l' option

Une instance de la classe Optional peut être créée à l'aide de ses méthodes statiques:

Optional optional = Optional.empty();

Renvoie un facultatif vide .

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

Renvoie un facultatif qui contient une valeur non nulle.

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.

Enfin, tout le code source de l'article est disponible à l'adresse over sur GitHub.