Java 中 null(Input/Output)Stream API 的用例是什么?

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

使用 Java 11,我可以将 InputStream 初始化为:

InputStream inputStream = InputStream.nullInputStream();

但我无法理解 InputStream.nullInputStream 的潜在用例或 OutputStream 的类似 API 即 OutputStream.nullOutputStream.

从 API Java 文档中,我可以弄清楚

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.

我进一步阅读了detailed release notes,其中指出:

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.

然而我不明白声明中的那些方法是什么……默默地执行这些方法以获得其他效果. (怪我没有亲身实践 APIs)

如果可能的话,有人可以通过示例帮助我理解拥有这样的输入或输出流有什么用处吗?


Edit:我在进一步浏览时发现的类似实现之一是 apache-commons 的 NullInputStream,它确实更好地证明了测试用例。

有时您希望有一个 InputStream 类型的参数,但又希望能够选择不向您的代码提供任何数据。在测试中模拟它可能更容易,但在生产中你可以选择绑定 null input 而不是用 ifs 和标志分散你的代码。

比较:

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

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

有了这个:

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

或者这个:

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

恕我直言,输出更有意义。输入可能更多是为了保持一致性。

这种方法称为空对象https://en.wikipedia.org/wiki/Null_object_pattern

我认为它是使用 null.

初始化流变量的更安全 (1) 和更具表现力 (2) 的替代方法
  1. 不用担心 NPE。
  2. [Output|Input]Stream 是一种抽象。为了 return 一个 null/empty/mock 流,您必须偏离核心概念,具体实施。

我认为nullOutputStream非常简单明了:只是为了丢弃输出(类似于> /dev/null)and/or用于测试(不需要发明一个OutputStream) .

一个(显然是基本的)示例:

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?

关于 nullInputStream 它可能更多地用于测试(我不喜欢模拟)和 API 需要输入流或(现在更有可能)提供不包含任何数据,或者您无法交付并且 null 不是一个可行的选择:

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

当您测试该导入器时,您可以只在那里使用 nullInputStream,而不是发明您自己的 InputStream 或使用模拟。这里的其他用例更像是 API 的解决方法或滥用 ;-)

关于 InputStream 的 return:这很有道理。如果您没有任何数据,您可能想要 return 那个 nullInputStream 而不是 null 这样调用者就不必处理 null 并且可以像他们一样阅读如果有数据。

最后,这些只是让我们的生活更轻松而无需添加其他依赖项的便捷方法 ;-) 正如其他人已经指出的那样 (comments/answers),它基本上是 null object pattern.

使用 null*Stream 可能还有一个好处,即测试执行得更快...如果您流式传输真实数据(当然...取决于大小等),您可能只会减慢测试速度不必要的,我们都希望测试快速完成,对吧? (有些人会在这里放模拟......嗯......)