――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 ».
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".
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")
}
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
map (...)
)
--Les données converties sont collectées dans une collection (collect (...)
)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.
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.
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.
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,
rateInfo.get ()
, mais un tel code peut être lu par analyse statique.Il y a des mérites tels que.
__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.
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.
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.
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.
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");
...
...
}
――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