What is the use case for null(Input/Output)Stream API in Java?
Asked Answered
W

3

30

With Java 11, I could initialize an InputStream as:

InputStream inputStream = InputStream.nullInputStream();

But I am unable to understand a potential use case of InputStream.nullInputStream or a similar API for OutputStream i.e. OutputStream.nullOutputStream.

From the API Javadocs, I could figure out that it

Returns a new InputStream that reads no bytes. The returned stream is initially open. The stream is closed by calling the close() method.

Subsequent calls to close() have no effect. While the stream is open, the available(), read(), read(byte[]), ... skip(long), and transferTo() methods all behave as if end of stream has been reached.

I went through the detailed release notes further which states:

There are various times where I would like to use methods that require as a parameter a target OutputStream/Writer for sending output, but would like to execute those methods silently for their other effects.

This corresponds to the ability in Unix to redirect command output to /dev/null, or in DOS to append command output to NUL.

Yet I fail to understand what are those methods in the statement as stated as .... execute those methods silently for their other effects. (blame my lack of hands-on with the APIs)

Can someone help me understand what is the usefulness of having such an input or output stream with a help of an example if possible?


Edit: One of a similar implementation I could find on browsing further is apache-commons' NullInputStream, which does justify the testing use case much better.

Whoremaster answered 23/11, 2018 at 14:52 Comment(6)
While asking the question, at some point in time I got this question as a suggestion to look at. Neither the links there land to proper sites and nor do I have an experience of working with the JDK where the line of code could be found. Maybe I am missing something, hence linking it here.Whoremaster
How would you pass a stream to file that doesn't exist to a method where this is optional for example. How would you tell a method to discard data - i.e. how do you do > /dev/null. Also - testing!Glassco
It's an implementation of the null object pattern. If you get what the pattern is about you'll get what's the use of null streams.Kerbela
@BoristheSpider I do somewhat understand theoretically what it means in terms of while willing to discard the data from a stream, but why should one be doing it(discarding data)? Could there be any services/systems I should be aware to know such an implementation? Testing - yes, does make sense to contribute to that as well.Whoremaster
@Kerbela Thanks for the link.I could correlate it to the discussion in the detailed release note over compatibility issues while making changes to create a null safe implementation for i/o streams. Would read about it further and digest it more wisely. Thanks again.Whoremaster
For example you run a System.exec and you want to pipe stdout to /dev/null but read stderr.Glassco
S
21

Sometimes you want to have a parameter of InputStream type, but also to be able to choose not to feed your code with any data. In tests it's probably easier to mock it but in production you may choose to bind null input instead of scattering your code with ifs and flags.

compare:

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

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

with this:

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);
    }
}

or this:

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

It makes more sense with output IMHO. Input is probably more for consistency.

This approach is called Null Object: https://en.wikipedia.org/wiki/Null_object_pattern

Shun answered 23/11, 2018 at 15:6 Comment(1)
And this is in no way new or Java-specific. Unix has long had /dev/null, for example, which serves much the same purpose at a different level. Windows has the little-known or used NUL device, which is similar (and which came from DOS).Illuminometer
D
11

I see it as a safer (1) and more expressive (2) alternative to initialising a stream variable with null.

  1. No worries about NPEs.
  2. [Output|Input]Stream is an abstraction. In order to return a null/empty/mock stream, you had to deviate from the core concept down to a specific implementation.
Disenable answered 23/11, 2018 at 15:14 Comment(1)
This is the answer. This is the same reason /dev/null exists on UNIX systems - because otherwise the "null" output would need to be treated differently.Glassco
T
2

I think nullOutputStream is very easy and clear: just to discard output (similar to > /dev/null) and/or for testing (no need to invent an OutputStream).

An (obviously basic) example:

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?

Regarding nullInputStream it's probably more for testing (I don't like mocks) and APIs requiring an input stream or (this now being more probable) delivering an input stream which does not contain any data, or you can't deliver and where null is not a viable option:

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

When you test that importer, you can just use a nullInputStream there, again instead of inventing your own InputStream or instead of using a mock. Other use cases here rather look like a workaround or misuse of the API ;-)

Regarding the return of an InputStream: that rather makes sense. If you haven't any data you may want to return that nullInputStream instead of null so that callers do not have to deal with null and can just read as they would if there was data.

Finally, these are just convenience methods to make our lifes easier without adding another dependency ;-) and as others already stated (comments/answers), it's basically an implementation of the null object pattern.

Using the null*Stream might also have the benefit that tests are executed faster... if you stream real data (of course... depending on size, etc.) you may just slow down your tests unnecessarily and we all want tests to complete fast, right? (some will put in mocks here... well...)

Tolyl answered 23/11, 2018 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.