Préface
Les pointeurs nuls sont les exceptions les plus courantes et les plus ennuyeuses que nous détestons. Afin d'empêcher les exceptions nuls de pointeurs, vous ne devez pas écrire beaucoup de jugements non nuls dans votre code.
Java 8 présente une nouvelle classe facultative. Pour éviter la survenue de pointeurs nuls, il n'est pas nécessaire d'écrire un grand nombre de jugements if(obj!=null) , tant que vous devez charger les données en option, c'est un conteneur qui enveloppe l'objet.
On dit qu'aucun programmeur qui a rencontré une exception de pointeur nul n'est pas un programmeur Java, et Null a en effet causé de nombreux problèmes. Java 8 présente une nouvelle classe appelée java.util.Optional pour éviter de nombreux problèmes causés par Null.
Voyons quel dommage une référence nul peut causer. Créez d'abord un ordinateur de classe, la structure est représentée dans la figure ci-dessous:
Que se passe-t-il lorsque nous appelons le code suivant?
String version = Computer.getSoundCard (). GetUSB (). GetVersion ();
Le code ci-dessus semble être bien, mais de nombreux ordinateurs (tels que Raspberry Pi) n'ont en fait pas de cartes son, donc appeler getSoundcard() lancera certainement une exception de pointeur nul.
Une méthode régulière mais mauvaise consiste à renvoyer une référence nul pour indiquer que l'ordinateur n'a pas de carte son, mais cela signifie que la méthode getUSB () sera appelée sur une référence vide, qui lancera évidemment une exception de contrôle pendant l'exécution du programme, provoquant la fin de l'exécution du programme. Pensez-y, à quel point il est gênant d'apparaître soudainement lorsque votre programme fonctionne sur un ordinateur client?
La grande informatique Tony Hoare a écrit: "Je pense que des citations nuls ont été créées en 1965, ce qui a entraîné un milliard de dollars de pertes. La plus grande tentation pour moi lors de l'utilisation de citations nulles était qu'il était facile à mettre en œuvre."
Alors, comment pouvons-nous éviter les exceptions du pointeur nul lorsque le programme est en cours d'exécution? Vous devez être alerte et vérifier constamment pour d'éventuels pointeurs nuls, comme ceci:
String version = "inconnu"; if (Computer! = null) {Soundcard Soundcard = Computer.getsoundCard (); if (Soundcard! = null) {USB USB = SoundCard.getUSB (); if (usb! = null) {version = usb.getVersion (); }}} Cependant, vous pouvez voir que le code ci-dessus a trop de vérifications nulles et la structure du code entière devient très laide. Mais nous devons utiliser ce jugement pour nous assurer qu'il n'y aura pas de pointeurs nuls lorsque le système est en cours d'exécution. Il est tout simplement ennuyeux de juger s'il y a beaucoup de références vides dans notre code commercial, et cela conduit également à une mauvaise lisibilité de notre code.
Si vous oubliez de vérifier si la valeur est vide, les références nulles ont également de grands problèmes potentiels. Dans cet article, je prouverai que l'utilisation de références nuls comme une représentation où les valeurs n'existent pas est une mauvaise façon. Nous avons besoin d'un meilleur modèle qui indique que la valeur n'existe pas, plutôt que d'utiliser à nouveau des références nulles.
Java 8 a présenté une nouvelle classe appelée java.util.Optional<T> , qui a été inspirée par la langue Haskell et la langue Scala. Cette classe peut contenir une valeur arbitraire, comme le montre la figure et le code ci-dessous. Vous pouvez considérer facultatif comme une valeur qui peut contenir une valeur. Si facultatif ne contient pas de valeur, il est vide, comme illustré dans la figure ci-dessous.
Public class Computer {Private Facultatif <SoundCard> Soundcard; public Facultatif <SoundCard> getSoundCard () {...} ...} public class Soundcard {private optional <usb> usb; public facultatif <usb> getUSB () {...}} public class usb {public String getVersion () {...}} Le code ci-dessus montre qu'un ordinateur peut remplacer une carte son (la carte son peut exister ou non). La carte son peut également inclure un port USB. Il s'agit d'une méthode d'amélioration, et le modèle peut plus clairement refléter qu'une valeur donnée peut ne pas exister.
Mais comment gérer l'objet Optional<Soundcard> ? Après tout, ce que vous voulez obtenir est le numéro de port USB. C'est très simple. La classe facultative contient certaines méthodes pour gérer si la valeur existe. Par rapport aux références nuls, la classe facultative vous oblige à gérer si la valeur est liée, évitant ainsi les exceptions de pointeur nul.
Il convient de noter que la classe facultative ne remplace pas les références nuls. Au contraire, pour rendre l'API conçue plus facile à comprendre, lorsque vous voyez la signature d'une fonction, vous pouvez déterminer si la valeur à transmettre à la fonction peut ne pas exister. Cela vous invite à ouvrir la classe facultative pour gérer la valeur réelle.
Adopter le mode facultatif
Après avoir dit tellement de choses, jetons un coup d'œil à un code! Voyons d'abord comment utiliser en option pour réécrire la détection traditionnelle de référence nul. À la fin de cet article, vous comprendrez comment utiliser facultatif.
String name = Computer.flatMap (Computer :: getSoundcard) .flatMap (Soundcard :: getUSB) .map (usb :: getVersion) .Orelse ("inconnu"); Créer des objets facultatifs
Un objet facultatif vide peut être créé:
Facultatif <SoundCard> SC = Facultatif.Empty ();
Ensuite, c'est de créer des valeurs non nulles contenant une contenu en option:
Soundcard SoundCard = new SoundCard (); Facultatif <SoundCard> SC = Facultational.of (Soundcard);
Si la carte son est nul, l'exception du pointeur nul sera lancée immédiatement (c'est mieux que de le lancer lorsque vous obtenez l'attribut de la carte son).
En utilisant OF Nullable, vous pouvez créer un objet facultatif qui peut contenir des références nuls:
Facultatif <SoundCard> SC = Facultatif.OfNullable (Soundcard);
Si la carte son est une référence nul, l'objet facultatif est vide.
Traitement des valeurs en option
Maintenant qu'il existe un objet facultatif, vous pouvez appeler la méthode correspondante pour gérer si la valeur dans l'objet facultatif existe. Par rapport à la détection nul, nous pouvons utiliser la méthode ifpresent (), comme ceci:
Facultatif <SoundCard> SoundCard = ...; SoundCard.ifpresent (System.out :: println);
De cette façon, il n'est pas nécessaire de faire une détection nul. Si l'objet facultatif est vide, aucune information ne sera imprimée.
Vous pouvez également utiliser la méthode isPresent() pour voir si l'objet facultatif existe vraiment. De plus, il existe également une méthode get () qui renvoie les valeurs incluses dans l'objet facultatif, s'il est présent. Sinon, une conception NosuchementElement sera lancée. Ces deux méthodes peuvent être utilisées ensemble comme celles suivantes pour éviter les exceptions:
if (SoundCard.ispresent ()) {System.out.println (SoundCard.get ());} Cependant, cette méthode n'est pas recommandée (elle n'a aucune amélioration par rapport à la détection nul). Ci-dessous, nous discuterons des modes de travail habituels.
Renvoie les valeurs par défaut et les opérations connexes
Lorsque vous rencontrez NULL, une opération régulière consiste à renvoyer une valeur par défaut, que vous pouvez utiliser les expressions ternaires pour implémenter:
Soundcard Soundcard = MayBesoundCard! = NULL? MayBesoundCard: new Soundcard ("Basic_Sound_Card"); Si vous utilisez un objet facultatif, vous pouvez utiliser orElse() pour remplacer. Lorsque facultatif est vide, orElse() peut renvoyer une valeur par défaut:
Soundcard Soundcard = MayBesoundCard.Orelse (New Soundcard ("Defaut")); De même, lorsque facultatif est vide, OrelSethrow () peut être utilisé pour lancer des exceptions:
Soundcard Soundcard = MayBesoundCard.OrelSethrow (illégalStateException :: new);
Utilisez le filtre pour filtrer les valeurs spécifiques
Nous appelons souvent une méthode d'objet pour juger de ses propriétés. Par exemple, vous devrez peut-être vérifier si le numéro de port USB est une valeur spécifique. Pour des raisons de sécurité, vous devez vérifier si l'utilisation médicale pointant vers l'USB est nul, puis appeler getVersion() , comme ceci:
USB USB = ...; if (usb! = Null && "3.0" .equals (usb.getVersion ())) {System.out.println ("OK");} Si vous utilisez facultatif, vous pouvez utiliser la fonction de filtre pour réécrire:
Facultatif <usb> peut-êtreusb = ...; peut-êtreusb.filter (usb -> "3.0" .equals (usb.getVersion ()) .ifpresent (() -> System.out.println ("ok")); La méthode du filtre nécessite un prédicat opposé en tant que paramètre. Si la valeur en option existe et satisfait à la prévision, la fonction filtrante renvoie une valeur qui satisfait la condition; Sinon, un objet facultatif vide sera retourné.
Utilisez la méthode de carte pour extraire et convertir les données
Un modèle commun consiste à extraire certaines propriétés d'un objet. Par exemple, pour un objet Soundcard, vous devrez peut-être obtenir son objet USB, puis déterminer son numéro de version. Habituellement, notre implémentation est comme ceci:
if (Soundcard! = null) {USB USB = SoundCard.getUSB (); if (usb! = null && "3.0" .equals (usb.getVersion ()) {System.out.println ("ok");}} Nous pouvons utiliser la méthode MAP pour remplacer cette détection NULL, puis extraire l'objet de type d'objet.
Facultatif <USB> USB = MayBesoundCard.map (Soundcard :: getUSB);
C'est la même chose que l'utilisation de la fonction MAP à l'aide de flux. L'utilisation du flux nécessite de passer une fonction en tant que paramètre à la fonction MAP, et la fonction passée sera appliquée à chaque élément du flux. Lors du streaming de l'espace et du temps, rien ne se passe.
La valeur contenue dans Facultatif sera convertie par la fonction transmise (voici une fonction qui obtient USB de la carte son). Si l'objet facultatif est l'espace-temps, rien ne se passera.
Ensuite, nous combinons la méthode MAP et la méthode du filtre pour filtrer les cartes son avec le numéro de version USB non 3.0.
MayBesoundCard.map (Soundcard :: getUSB) .Filter (USB -> "3.0" .equals (usb.getVersion ()) .ifpresent (() -> System.out.println ("ok")); De cette façon, notre code commence à ressembler un peu à ce que nous avons donné au début, sans détection nul.
Passer un objet facultatif à l'aide de la fonction FlatMap
Désormais, un exemple de la façon de refacter le code en utilisant facultatif a été introduit. Alors, comment devrions-nous implémenter le code suivant de manière sûre?
String version = Computer.getSoundCard (). GetUSB (). GetVersion ();
Notez que le code ci-dessus extrait tous un autre objet d'un objet, qui peut être implémenté à l'aide de la fonction MAP. Dans l'article précédent, nous avons configuré un objet Optional<Soundcard> dans l'ordinateur, et SoundCard contient un objet Optional<USB> , afin que nous puissions refactoriser le code de cette manière
String version = Computer.map (Computer :: getSoundcard) .map (Soundcard :: getUSB) .map (usb :: getVersion) .Orelse ("inconnu"); Malheureusement, le code ci-dessus compile les erreurs, alors pourquoi? La variable informatique est de type Optional<Computer> , il n'a donc aucun problème à appeler la fonction de carte. Cependant, getSoundcard() renvoie un objet Optional<Soundcard> , qui renvoie un objet de type Optional<Optional<Soundcard>> . Une fois la deuxième fonction de carte appelée, l'appel à getUSB() devient illégal.
La figure suivante décrit ce scénario:
L'implémentation du code source de la fonction MAP est la suivante:
public <u> MAPELLE OPTIONNEL <u> (fonction <? Super T ,? étend u> mappeur) {objets.RequiRenonnull (mappeur); if (! isPresent ()) return vide (); else {return facultatif.ofNullable (mapper.Apply (valeur)); }} On peut voir que la fonction MAP appellera à nouveau Optional.ofNullable() , ce qui entraîne le retour de Optional<Optional<Soundcard>>
FORMATIF FORMATION DE LA FONCTION FLATMAP, qui est conçue pour convertir la valeur de l'objet facultatif (comme une opération de carte), puis comprimez une facultatif en option à deux niveaux. La figure suivante montre la différence entre les objets facultatifs dans la conversion de type en appelant MAP et FlatMap:
Nous pouvons donc écrire ceci:
String version = Computer.flatMap (Computer :: getSoundCard) .flatMap (Soundcard :: getUSB) .map (usb :: getVersion) .Orelse ("inconnu"); Le premier FlatMap garantit que le retour est Optional<Soundcard> plutôt que Optional<Optional<Soundcard>> , et le deuxième flatmap implémente la même fonction pour que le retour soit Optional<USB> . Notez que map() est appelé la troisième fois, car getVersion() renvoie un objet String au lieu d'un objet facultatif.
Nous réécrivons enfin le code laid de vérifications nulles imbriquées que nous venons de commencer à utiliser, ce qui est très lisible, et évite également la survenue d'exceptions de pointeur nul.
Résumer
Dans cet article, nous adoptons la nouvelle classe java.util.Optional<T> fournie par Java 8. L'intention initiale de cette classe n'est pas de remplacer les références nuls, mais d'aider les concepteurs à concevoir de meilleures API. Lisez simplement la signature de la fonction et sachez si la fonction accepte une valeur qui peut exister ou non. De plus, facultatif, vous oblige à activer facultatif, puis à gérer si la valeur existe, ce qui fait que votre code évite les exceptions potentielles du pointeur nul.
D'accord, ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.