Каков вариант использования нулевого (входного/выходного) потокового API в Java?

В Java 11 я мог бы инициализировать InputStream следующим образом:

InputStream inputStream = InputStream.nullInputStream();

Но я не могу понять потенциальный вариант использования InputStream.nullInputStream или аналогичного API для OutputStream то есть OutputStream.nullOutputStream.

Из API Javadocs я мог понять, что это

Возвращает новый InputStream который не читает байтов. Возвращенный поток изначально открыт. Поток закрывается путем вызова метода close().

Последующие вызовы close() имеют никакого эффекта. Пока поток открыт, методы available(), read(), read(byte[]) ,... skip(long) и transferTo() ведут себя так, как будто достигнут конец потока.

Далее я ознакомился с подробными примечаниями к выпуску, в которых говорится:

В разное время я хотел бы использовать методы, которые требуют в качестве параметра целевого объекта OutputStream/Writer для отправки вывода, но хотели бы выполнить эти методы без вывода сообщений для их других эффектов.

Это соответствует способности в Unix перенаправлять вывод команды в /dev/null или в DOS для добавления вывода команды в NUL.

Тем не менее, я не понимаю, что это за методы в заявлении, как указано как.... выполнять эти методы молча для их других эффектов. (обвините меня в отсутствии практического применения API)

Может ли кто-нибудь помочь мне понять, в чем полезность такого потока ввода или вывода с помощью примера, если это возможно?


Редактировать: Одной из похожих реализаций, которую я мог бы найти при дальнейшем просмотре, является NullInputStream Apache-commons, который намного лучше оправдывает вариант использования тестирования.

Ответ 1

Иногда вы хотите иметь параметр типа InputStream, но также и иметь возможность не передавать свой код какими-либо данными. В тестах, вероятно, проще его смоделировать, но в производстве вы можете связать нулевой ввод, а не разбрасывать код с помощью if и flags.

Для сравнения:

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

Ответ 2

Я вижу это как более безопасную (1) и более выразительную (2) альтернативу инициализации переменной потока с null.

  1. Не беспокойтесь о NPE.
  2. [Output|Input]Stream - это абстракция. Чтобы вернуть поток null/empty/mock, вам нужно было отклониться от основной концепции до конкретной реализации.

Ответ 3

Я думаю, что nullOutputStream очень прост и понятен: просто отбросить вывод (похожий на >/dev/null) и/или для тестирования (не нужно изобретать 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 он, вероятно, больше 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: это скорее имеет смысл. Если у вас нет никаких данных, вы можете захотеть вернуть этот nullInputStream вместо null чтобы вызывающим абонентам не приходилось иметь дело с null и они могли просто читать, как если бы были данные.

Наконец, это просто удобные методы, облегчающие жизнь без добавления другой зависимости ;-) и, как уже говорили другие (комментарии/ответы), это в основном реализация шаблона нулевого объекта.

Использование null*Stream также может иметь преимущество в том, что тесты выполняются быстрее... если вы выполняете потоковую передачу реальных данных (конечно... в зависимости от размера и т.д.), Вы можете просто без необходимости замедлять свои тесты, и мы все хотим, чтобы тесты выполнялись завершить быстро, верно? (некоторые тут насмехаются... ну...)