Écrire du code de type Java8 en Java8

Vue d'ensemble (de quoi parlez-vous)

――Lorsque je révise le code, j'utilise les fonctionnalités Java8 (Stream API, facultatif, etc.), mais je vois souvent du code qui est écrit de la même manière qu'avant Java7. ――Je veux que tout le monde écrive un code de type __Java8 __. C'est un gaspillage sinon. (* Le standard de "Java 8-ish" est relativement (assez?) L'opinion personnelle est incluse) «C'est difficile à transmettre même si vous utilisez tous les mots, alors faisons un exemple. __ ← ce __ «À propos, faisons un exemple d '« utilisation forcée des fonctionnalités de Java 8, ce qui aggrave les choses ».

Public cible

Notice Le contenu de cet article n'est pas nouveau et certains langages autres que Java peuvent être écrits de manière plus sophistiquée. Cet article s'adresse aux "personnes qui ont migré de Java 7 ou version antérieure vers Java 8 mais qui ne sont pas habituées à écrire en utilisant Stream et Optional".

Modèles qui peuvent ressembler davantage à Java 8

On suppose qu'il existe un objet produit comme celui-ci et qu'un traitement y est effectué.

Item.java


//Classe de produit
public class Item {
  String name; //Nom du produit
  BigDecimal price; //Prix du produit
  String currency; //devise du champ de prix("JPY"Et"USD")
}

Je viens de remplacer l'instruction for étendue par forEach

Supposons que vous vouliez faire quelque chose comme "convertir des données et les stocker dans une autre liste". Avant Java7, cela ressemblait à ↓.

Comment écrire avant java7


//Convertir les produits Yen japonais en USD
BigDecimal jpyToUsd = BigDecimal.valueOf(100);

List<Item> usdItems = new ArrayList<>();

for(Item jpyItem : jpyItems) {
    Item usdItem = new Item();
    usdItem.setName(jpyItem.getName());
    usdItem.setPrice(jpyItem.getPrice().multiply(jpyToUsd));
    usdItem.setCurrency("USD");
    usdItems.add(usdItem);
}

Donc, après avoir été dit, "Parce que c'est Java 8, faites-le avec Stream sans utiliser pour", ça devient comme ça.

forEach seulement


BigDecimal jpyToUsd = BigDecimal.valueOf(100);

List<Item> usdItems = new ArrayList<>();

jpyItems.forEach(jpyItem -> {
    Item usdItem = new Item();
    usdItem.setName(jpyItem.getName());
    usdItem.setPrice(jpyItem.getPrice().multiply(jpyToUsd));
    usdItem.setCurrency("USD");
    usdItems.add(usdItem);
});

Ce que vous faites n'est pas différent de l'instruction Extended for. En réécrivant ceci en gardant à l'esprit "l'opération intermédiaire" et "l'opération de terminaison" de Stream, cela devient comme ça.

Java8-ish


BigDecimal jpyToUsd = BigDecimal.valueOf(100);

//Réalisé en combinant la carte et la collecte sans utiliser forEach
List<Item> usdItems = jpyItems.stream()
        .map(jpyItem -> {
            Item usdItem = new Item();
            usdItem.setName(jpyItem.getName());
            usdItem.setPrice(jpyItem.getPrice().multiply(jpyToUsd));
            usdItem.setCurrency("USD");

            return usdItem;
        })
        .collect(Collectors.toList());

(* Les contre-mesures pour le fait que le contenu de la carte soit gras seront décrites plus loin)

En faisant cela

Il devient plus clair qu'il devient plus lisible. forEach peut tout faire et est utile, mais considérez s'il existe une autre méthode appropriée.

lambda est grand

Dans l'exemple précédent, le lambda dans la carte est grand, la chaîne de méthodes du Stream devient longue et l'ensemble est difficile à voir et la lisibilité est faible.

Si vous demandez: "Alors, devrais-je supprimer lambda comme variable?"

Variable lambda


Function<Item,Item> convertToUsdItem = jpyItem -> {
    Item usdItem = new Item();
    usdItem.setName(jpyItem.getName());
    usdItem.setPrice(jpyItem.getPrice().multiply(jpyToUsd));
    usdItem.setCurrency("USD");

    return usdItem;
};

List<Item> usdItems = jpyItems.stream()
    .map(convertToUsdItem)
    .collect(Collectors.toList());

Stream est toujours rafraîchissant, mais il est moins testable lorsque vous voulez tester le traitement de convertToUsdItem, il est donc plus pratique d'utiliser une méthode normale, et c'est plus facile à lire personnellement. (Cela peut être une question de goût concernant la lisibilité)

Découpez comme une méthode normale


public Item convertToUsdItem(Item jpyItem) {
    Item usdItem = new Item();
    usdItem.setName(jpyItem.getName());
    usdItem.setPrice(jpyItem.getPrice().multiply(jpyToUsd));
    usdItem.setCurrency("USD");

    return usdItem;
}

List<Item> usdItems = jpyItems.stream()
    .map(this::convertToUsdItem)
    .collect(Collectors.toList());

En utilisant des références de méthode, vous pouvez écrire un Stream aussi simplement qu'un lambda.

J'utilise uniquement certains champs de l'objet cible, mais je ne suis pas map

Par exemple, lorsque je considère un processus tel que "afficher le nom du produit en standard pour les produits dont le nom de produit commence par" A "", je vois parfois un code comme celui-ci.

python


items.steam()
    .filter(item -> item.getName().startsWith("A"))
    .forEach(item -> System.out.println(item.getName()));

Puisque vous souhaitez utiliser uniquement le nom du produit, vous n'avez pas besoin d'acheminer l'objet article jusqu'à la fin, il est donc plus facile de suivre en extrayant d'abord le champ de nom avec la carte.

Extraire uniquement le nom avec la carte et le processus


items.stream()
    .map(Item::getName)
    .filter(name -> name.startsWith("A"))
    .forEach(System.out::println);

En faisant cela,

—— Vous pouvez comprendre à un stade précoce que "seuls les noms de produits sont traités"

Il y a des mérites tels que.

J'ai créé une méthode qui renvoie null et vérifiée pour null par son appelant

Lors du traitement de la valeur de retour de findFirst de l'API Stream, ou lors de l'utilisation d'une méthode de la bibliothèque Java 8 qui renvoie Optional, vous devez utiliser Optional, que ce soit ou non. Cependant, lorsque j'apprenais juste Java8, il semble que lorsque j'essaie d'implémenter une méthode moi-même, je ne pense souvent pas "définissons le type de retour sur Facultatif".

Par exemple, supposons que vous créez une méthode qui "récupère les informations de taux de change du cache et renvoie la valeur par défaut (1) si le cache n'existe pas".

Obtenez le taux de change en espèces(version nulle)



public RateInfo getFromCache(String fromCurrency, String toCurrency) {
    //Renvoie son objet RateInfo s'il existe dans le cache, null sinon
}

public BigDecimal retrieveRate(String fromCurrency, String toCurrency) {

    RateInfo rateInfo = getFromCache(fromCurrency, toCurrency);

    if(rateInfo != null) {
        return rateInfo.getRate();
    }

    return BigDecimal.ONE;
}

Ici, la méthode getFromCache se comporte comme" renvoie son objet RateInfo s'il existe dans le cache, ou null s'il n'existe pas. " Par conséquent, une instruction if pour un contrôle nul est requise chez l'appelant. Et parfois, je ne vérifie pas la valeur nulle et cela devient gluant.

C'est pourquoi Optionnel entre en jeu ici.

Obtenez le taux de change en espèces(Version optionnelle)


public Optional<RateInfo> getFromCache(String fromCurrency, String toCurrency) {
    //Le résultat de la tentative de récupération de l'objet RateInfo du cache est facultatif<RateInfo>Retour en tant qu'objet
}

public BigDecimal retrieveRate(String fromCurrency, String toCurrency) {
    Optional<RateInfo> rateInfo = getFromCache(fromCurrency, toCurrency);

    return rateInfo.map(RateInfo::getRate).orElse(BigDecimal.ONE);
}

En faisant cela,

Il y a des mérites tels que.

Modèles qui ne devraient pas être forcés d'utiliser les fonctionnalités de Java 8

__for et null check Un cas dans lequel il est facile de tomber quand il ressemble à un homme qui tue absolument __. Java8's Stream et Optional ne sont souvent pas aussi cool que ceux des autres langages, et je suis accro à essayer de les couvrir.

Remplacer l'instruction for par index avec Stream

Par exemple

pour instruction avec index


for(int i=0; i < items.size(); i++) {
    System.out.println(
        String.valueOf(i) + " : " + items.get(i).getName());
}

En raison de l'enthousiasme suscité par le traitement du type "Je vais me passer de l'instruction for!"

Faites de votre mieux avec Stream


//Génération de compteur de boucle avec IntStream
IntStream.range(0, items.size())
    .forEach(i -> System.out.println(
        String.valueOf(i) + " : " + items.get(i).getName());

//Faites de votre mieux avec Atomic Integer et pour Each
AtomicInteger i = new AtomicInteger();
items.forEach(item -> System.out.println(
        String.valueOf(i.getAndIncrement()) + " : " + item.getName());

Cela devient comme. Le premier est à l'origine destiné à être exprimé en tant que traitement Stream des éléments, mais il est accessible en utilisant get (i) dans l'IntStream pour la génération d'index, il n'est donc pas différent d'une instruction for normale et est difficile à lire. (J'écrivais de cette façon, mais je l'ai arrêté pour la raison mentionnée ci-dessus)

Ce dernier est le dernier, et «utiliser AtomicInteger car le type primitif int ne peut pas être incrémenté en lambda» n'est pas recommandé car il semble s'écarter de l'utilisation originale d'AtomicInteger.

Opération de collecte avec index en Java ([Kotlin's this](http://qiita.com/opengl-8080/items/36351dca891b6d9c9687#indexed%E7%B9%B0%E3%82%8A%E8%BF%94%E3] % 81% 97% E5% 87% A6% E7% 90% 86% E3% 81% AB% E3% 83% AB% E3% 83% BC% E3% 83% 97% E3% 82% A4% E3% 83 % B3% E3% 83% 87% E3% 83% 83% E3% 82% AF% E3% 82% B9% E3% 82% 82% E6% B8% A1% E3% 81% 99)) J'aimerais pouvoir l'utiliser Malheureusement, il n'a pas encore été implémenté, donc pour l'instant, il semble préférable d'utiliser l'instruction for avec obéissance plutôt que d'utiliser de force l'API Stream.

ʻOptional.ofNullable` juste pour la vérification de null

Certaines bibliothèques et méthodes plus anciennes disponibles avant Java 7 peuvent renvoyer null comme valeur de retour. Pour une telle méthode, si vous voulez juste vérifier si la valeur de retour est nulle, et que vous vous sentez comme "je n'écris pas ʻif (a! = Null)` pour rien! ", Le code suivant s'affiche. C'est fait.

Évaluer en enveloppant la valeur de retour dans facultatif



// getText()Peut retourner null
String text = getText();

//Enveloppez-le dans Facultatif et vérifiez-le. N'utilisez pas la valeur du contenu
if(Optional.ofNullable(text).isPresent()) {
    return "OK";   
}

return "NG";

ʻOptional.isPresent () C'est correct de l'utiliser, mais c'est un peu plus ennuyeux parce que le code est plus long que la vérification normale de null. Je pense que ʻif (text! = Null) est bien ici.

Une petite partie inquiétante - comment utiliser comme l'argument par défaut

Optionnel est principalement utilisé comme "valeur de retour d'une méthode" en tant que concept de conception, et il semble que d'autres utilisations (par exemple, utilisées comme type d'argument) ne soient pas recommandées. r.f stackoverflow - Why java.util.Optional is not Serializable, how to serialize the object with such fields

Certes, même si vous utilisez le type Facultatif comme argument, l'objet optionnel que vous avez passé peut être nul, donc ce n'est pas très délicieux. Cependant, il y a quelque chose à propos de l'argument, "N'est-il pas possible d'utiliser ce type d'Optionnel?" C'est ainsi qu'il est utilisé comme argument __default __.

Exprimer un pseudo argument par défaut avec facultatif


//Renvoie le produit cible converti au prix de la devise spécifiée
public Item convert(Item item, String toCurrency) {
     //Utilisez USD si aucune devise n'est spécifiée
     String currency = Optional.ofNullable(toCurrency).orElse("USD");

     ...
     ...
}

Il peut y avoir des opinions qui "utilisent la surcharge", mais comme la surcharge ne peut pas être utilisée pour la méthode d'implémentation de l'interface de la bibliothèque tierce qui est spécifiée comme méthode de rappel, j'envisage comment l'utiliser comme ça.

Postscript (30/08/2017)

Comme indiqué dans Comment, il peut être préférable d'utiliser une autre méthode.

Utilisez la méthode "valeur par défaut si nul"


//Renvoie le produit cible converti au prix de la devise spécifiée
public Item convert(Item item, String toCurrency) {
     // java.util.Objects.toString
     String currency1 = Objects.toString(toCurrency,"USD");

     // org.apache.commons.lang3.ObjectUtils.defaultIfNull
     String currency2 = ObjectUtils.defaultIfNull(toCurrency, "USD");

     ...
     ...
}

finalement

――Il est intéressant d'explorer différentes manières d'écrire, étant donné que l'API Stream et Optional peuvent être écrits d'une manière plus efficace et plus facile à comprendre qu'auparavant, plutôt que d'ajouter simplement une classe de méthode. ――Cependant, il y a de nombreuses parties qui ne sont pas cool au moment de Java 8, il est donc important de rechercher «juste la bonne utilisation» au lieu de l'utiliser aveuglément.

Recommended Posts

Écrire du code de type Java8 en Java8
Ecrire des rappels de vol en Java
Java avec Visual Studio Code
Devinez le code de caractère en Java
Environnement Java Spring dans vs Code
Écrire du code de test avec Spring Boot
Exécuter du code Java de manière scriptée
CONSEILS relatifs au code Java
Partition en Java
Changements dans Java 11
Janken à Java
Exemple de code Java 04
Toutes les mêmes chaînes de code de hachage en Java
Exemple de code Java 01
Code de caractère Java
Taux circonférentiel à Java
[Mac] Installer Java dans Visual Studio Code
FizzBuzz en Java
Ajouter l'option --enable-preview dans Java dans Visual Studio Code
Techniques de lecture du code source Java dans Eclipse
Analyse de code statique par Checkstyle avec Java + Gradle
Code pour échapper aux chaînes JSON en Java
Essayez d'utiliser Sourcetrail (version win) avec du code Java
Ecrire du code de liaison Selenium Java à l'aide de Silk WebDriver
[AtCoder Problem-ABC001] Observation du vent C-Do en Java [Code]
Comment écrire Java String # getBytes dans Kotlin?
[Angoisse du débutant Java] Code difficile à tester implémenté dans Junit
[Mac] Installer Java dans Visual Studio Code (VS Code)
Lire JSON en Java
Implémentation de l'interpréteur par Java
Faites un blackjack avec Java
Application Janken en Java
Programmation par contraintes en Java
Mettez java8 dans centos7
Joindre des tableaux en Java
"Hello World" en Java
Interface appelable en Java
Commentaires dans la source Java
Fonctions Azure en Java
Formater XML en Java
Simple htmlspecialchars en Java
Implémentation Boyer-Moore en Java
Hello World en Java
Utiliser OpenCV avec Java
Mémorandum WebApi avec Java
Détermination de type en Java
Exécuter des commandes en Java (ping)
Divers threads en java
API Zabbix en Java
Art ASCII à Java
Comparer des listes en Java
POST JSON en Java
Exprimer l'échec en Java
Implémenter CustomView dans le code
Créer JSON en Java
Manipulation de la date dans Java 8
Nouveautés de Java 8
Utiliser PreparedStatement en Java
Nouveautés de Java 9,10,11
Exécution parallèle en Java