Собирать необработанный поток в типизированную коллекцию?

Я вызываю библиотечный метод, который возвращает необработанный Stream. Я знаю тип элементов в потоке и хочу собрать их в коллекцию с объявленным типом элемента. Каков хороший или не очень ужасный способ сделать это?

Минимальный воспроизводимый пример

Ради воспроизводимого примера предположим, что я хочу вызвать этот метод:

@SuppressWarnings("rawtypes")
static Stream getStream() {
    return Stream.of("Example");
}

Я надеялся, что это сработает, и я не понял, почему это не так:

    List<String> stringList = getStream().map(s -> (String) s).collect(Collectors.toList());

Я получаю сообщение Несоответствие типов: невозможно преобразовать объект в список. Почему?

Обходной путь

Непроверенный бросок работает. Чтобы сузить бит, который необходимо пометить как преднамеренно непроверенный, мы можем сделать

    @SuppressWarnings("unchecked")
    Stream<String> stringStream = getStream();
    List<String> stringList = stringStream.collect(Collectors.toList());

Как в Java 8, так и в Java 11 возвращаемый список представляет собой java.util.ArrayList и содержит ожидаемый элемент String.

Что мой поиск дал или нет

Я попытался найти в своей поисковой системе такие термины, как java collection raw stream. Ни к чему полезному это меня не привело. Существует закрытая ошибка Java (ссылка внизу), в которой упоминается, что как только вы работаете с необработанными типами, все становится необработанным. Но эта полностью необработанная версия тоже не работает:

    List stringList = getStream().collect(Collectors.toList());

Я по-прежнему получаю сообщение Несоответствие типов: невозможно преобразовать объект в список. Как так?

Мы кодируем для Java 8 (еще немного).

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

Предыстория: Гибернация 5

Метод, который я хочу вызвать, это org.hibernate.query.Query.getResultStream() для необработанного Query (параметр типа Query отсутствует). Я хочу получить поток, потому что у меня есть особые требования к типу коллекции, в которую я собираю. В моем производственном коде я использую Collectors.toCollection() (а не Collectors.toLlist(), как в примере в этом вопросе). Кстати, при написании этого вопроса я нашел способ убедить Hibernate вернуть типизированный поток. Я все еще нашел вопрос достаточно интересным, чтобы опубликовать.

Связь


person Ole V.V.    schedule 09.01.2021    source источник
comment
Необработанный тип стирает все дженерики, используемые в этом типе. Это по дизайну. Я не думаю, что есть какой-то способ обойти непроверенный состав.   -  person Slaw    schedule 09.01.2021
comment
Я мог бы что-то упустить, но почему бы вам просто не запустить сам поток, если вы знаете, что в нем есть? ((Stream<String>)getStream())   -  person Sweeper    schedule 09.01.2021
comment
@Sweeper Разве это не то, что я неявно делаю в своем обходном пути? Это все еще выглядит как обходной путь для меня. Я слишком придирчив?   -  person Ole V.V.    schedule 09.01.2021
comment
Ах да, но есть ли на самом деле непроверенное предупреждение? Моя IDE не выдала ни одного... (может быть, я не настроил?)   -  person Sweeper    schedule 09.01.2021
comment
@Sweeper Мой рабочий Eclipse и домашний Eclipse в этом отношении вели себя по-разному, так что да, вероятно, вопрос о предпочтениях.   -  person Ole V.V.    schedule 09.01.2021
comment
@Sweeper Компиляция напрямую с javac выводит Note: Main.java uses unchecked or unsafe operations. для меня.   -  person Slaw    schedule 09.01.2021
comment
Превратите Stream в Stream<?>. Это единственный способ избавиться от необработанного типа без операции unchecked. После этого map можно использовать по назначению.   -  person Holger    schedule 10.01.2021
comment
Эй, @Holger, это действительно похоже на то, куда я хочу пойти. Не стесняйтесь публиковать в качестве ответа, если у вас есть время. Большое спасибо.   -  person Ole V.V.    schedule 10.01.2021
comment
Этот вопрос имеет ожидаемое поведение, конкретную проблему (вставленное сообщение об ошибке), минимальный воспроизводимый пример, поиск и исследование (найден обходной путь). Просто чтобы я мог продолжать совершенствоваться (мой послужной список вопросов выглядит не очень хорошо): что нужно, чтобы вопрос не получил отрицательного ответа? (я просмотрел это и это, не приближаясь к пониманию.)   -  person Ole V.V.    schedule 26.02.2021


Ответы (1)


Подсказка содержится в отчете об ошибках Java: как только я использую необработанный тип, все становится необработанным. Stream.collect() тоже является универсальным методом, так что его необработанная версия возвращает — Object, не удивляйтесь.

Метод Stream, который я вызываю, объявлен следующим образом:

    <R,A> R collect​(Collector<? super T,A,R> collector)

Таким образом, он возвращает R, а что такое R, он может собрать из переданного сборщика (будь то Collectors.toList() или какой-то Collectors.toCollection()). Как только все стало сырым, вычет больше не работает. И collect() возвращает Object.

В заключение: дженерики — это хорошо. Когда я не могу избежать получения чего-то необработанного, конвертируйте его в его общий аналог как можно раньше.

EDIT: С благодарностью Sweeper и Holger за ценный вклад в комментарии, это мое предпочтительное решение:

    Stream<?> wildcardStream = getStream();
    List<String> stringList = wildcardStream.map(s -> (String) s)
            .collect(Collectors.toList());
    System.out.println(stringList.getClass());
    System.out.println(stringList);

Вывод на Java 11:

class java.util.ArrayList
[Example]

Приведение Stream к Stream<?> (неявно или явно) избавляет от необработанного типа без непроверенной операции. После этого map() можно использовать по назначению. И нет необходимости в @SuppressWarnings("unchecked").

Это также означает, что решение для моего исходного сценария Hibernate заключается в приведении объекта Query к Query<?> перед вызовом getResultStream(), а не только приведении потока. Затем приведите каждый отдельный объект в map() в потоковой операции так же, как описано выше.

person Ole V.V.    schedule 09.01.2021