web-dev-qa-db-fra.com

Quel est le cas d'utilisation de l'API Stream (entrée / sortie) nulle en Java?

Avec Java 11, je pourrais initialiser un InputStream comme:

InputStream inputStream = InputStream.nullInputStream();

Mais je ne peux pas comprendre un cas d'utilisation potentiel de InputStream.nullInputStream ou une API similaire pour OutputStream c'est-à-dire OutputStream.nullOutputStream .

De l'API Javadocs, je pouvais comprendre qu'il

Renvoie un nouveau InputStream qui ne lit aucun octet. Le flux renvoyé est initialement ouvert. Le flux est fermé en appelant la méthode close ().

Les appels suivants à close() n'ont aucun effet. Pendant que le flux est ouvert, la available(), read(), read(byte[]), ... skip(long) et transferTo() toutes les méthodes se comportent comme si la fin du flux était atteinte.

J'ai parcouru les notes de version détaillées qui indiquent:

Il y a plusieurs fois où j'aimerais utiliser des méthodes qui nécessitent en paramètre un OutputStream/Writer cible pour envoyer la sortie, mais je voudrais exécuter ces méthodes en silence pour leurs autres effets.

Cela correspond à la possibilité sous Unix de rediriger la sortie de commande vers/dev/null, ou sous DOS pour ajouter la sortie de commande à NUL.

Pourtant, je n'arrive pas à comprendre ce que sont ces méthodes dans l'instruction comme indiqué comme .... exécutez ces méthodes en silence pour leurs autres effets. (blâmer mon manque de pratique avec les API)

Quelqu'un peut-il m'aider à comprendre l'utilité d'avoir un tel flux d'entrée ou de sortie avec un exemple si possible?


Edit: Une des implémentations similaires que j'ai pu trouver en naviguant plus loin est Apache-commons ' NullInputStream , ce qui justifie beaucoup mieux le cas d'utilisation du test.

28
Naman

Parfois, vous voulez avoir un paramètre de type InputStream, mais aussi pouvoir choisir de ne pas alimenter votre code avec des données. Dans les tests, il est probablement plus facile de se moquer de lui, mais en production, vous pouvez choisir de lier une entrée nulle au lieu de disperser votre code avec ifs et drapeaux .

comparer:

class ComposableReprinter {
    void reprint(InputStream is) throws IOException {
        System.out.println(is.read());
    }

    void bla() {
        reprint(InputStream.nullInputStream());
    }
}

avec ça:

class ControllableReprinter {
    void reprint(InputStream is, boolean for_real) throws IOException {
        if (for_real) {
            System.out.println(is.read());
        }
    }
    void bla() {
        reprint(new BufferedInputStream(), false);
    }
}

ou ca:

class NullableReprinter {
    void reprint(InputStream is) throws IOException {
        if (is != null) {
            System.out.println(is.read());
        }
    }
    void bla() {
        reprint(null);
    }
}

Cela a plus de sens avec la sortie IMHO. L'entrée est probablement plus pour la cohérence.

Cette approche est appelée Null Object : https://en.wikipedia.org/wiki/Null_object_pattern

18
Milo Bem

Je le vois comme une alternative plus sûre (1) et plus expressive (2) à l'initialisation d'une variable de flux avec null.

  1. Pas de soucis pour les NPE.
  2. [Output|Input]Stream est une abstraction. Pour renvoyer un flux nul/vide/simulé, vous avez dû vous écarter du concept de base vers une implémentation spécifique.
12
Andrew Tobilko

Je pense que nullOutputStream est très simple et clair: juste pour supprimer la sortie (similaire à > /dev/null) et/ou pour les tests (pas besoin d'inventer un OutputStream).

Un exemple (évidemment basique):

OutputStream out = ... // an easy way to either print it to System.out or just discard all prints, setting it basically to the nullOutputStream
out.println("yeah... or not");
exporter.exportTo(out); // discard or real export?

En ce qui concerne nullInputStream c'est probablement plus pour tester (je n'aime pas les maquettes) et les API nécessitant un flux d'entrée ou (ce qui est maintenant plus probable) pour fournir un flux d'entrée qui ne contient aucune donnée, ou vous ne pouvez pas livrer et où null n'est pas une option viable:

importer.importDocument("name", /* input stream... */);
InputStream inputStream = content.getInputStream(); // better having no data to read, then getting a null

Lorsque vous testez cet importateur, vous pouvez simplement utiliser un nullInputStream, là encore au lieu d'inventer votre propre InputStream ou au lieu d'utiliser une maquette. Les autres cas d'utilisation ici ressemblent plutôt à une solution de contournement ou à une mauvaise utilisation de l'API ;-)

Concernant le retour d'un InputStream: cela a plutôt du sens. Si vous n'avez aucune donnée, vous voudrez peut-être renvoyer ce nullInputStream au lieu de null afin que les appelants n'aient pas à traiter null et puissent simplement lire comme ils le feraient si il y avait des données.

Enfin, ce ne sont que des méthodes pratiques pour nous faciliter la vie sans ajouter une autre dépendance ;-) et comme d'autres l'ont déjà dit (commentaires/réponses), il s'agit essentiellement d'une implémentation du modèle d'objet nul .

En utilisant le null*Stream pourrait également avoir l'avantage que les tests sont exécutés plus rapidement ... si vous diffusez des données réelles (bien sûr ... selon la taille, etc.), vous pouvez simplement ralentir vos tests inutilement et nous voulons tous que les tests se terminent rapidement, droite? (certains se moqueront ici ... eh bien ...)

3
Roland