Почему эти лямбды Java 8 ведут себя по-разному во время преобразования типов? - PullRequest
0 голосов
/ 03 февраля 2019
Stream st = Stream.of(1,2,3,4);

Создает ли это поток целых чисел (путем вывода во время выполнения) или поток объектов?

st.collect(Collectors.averagingInt((Integer x)->x));

Эта строка компилируется нормально.

st.forEach((Integer x)-> System.out.println(x));

Эта строка не компилируется.Это дает ошибку компиляции в (Integer x), говоря ожидаемый объект, но нашел Integer.Почему?

Почему он не жаловался на то же самое для метода averagingInt, описанного выше?

averagingInt принимает интерфейс ToIntFunction с необработанным ссылочным типом как <? super T> и допускает целочисленное значение.Пока forEach использует интерфейс Consumer с тем же необработанным ссылочным типом, что и <? super T>, а компилятор жалуется на значение аргумента Integer.

Ответы [ 3 ]

0 голосов
/ 03 февраля 2019

Как было сказано выше, вы пытаетесь использовать raw Stream.Если вы хотите выполнить итерацию по Integer, вам нужно указать универсальный для Stream.

Например,

Stream<Integer> st = Stream.of(1,2,3,4);

Затем он скомпилируется.

Этот вероятностный эквивалент эквивалентен проблеме с повторением общей выборки в старом стиле

Collection values = Arrays.asList(1,2,3,4);
for(Integer v: values){
    System.out.println(v);
}

Приведенный выше код не будет компилироваться, поскольку значения - это список объектов, а не целые числа.

Что касается averagingInt , то он будет скомпилирован, даже если вы предоставите список строк ,Например,

    Stream st = Stream.of("1","2","3","4");
    st.collect(Collectors.averagingInt((Integer x)->x));

Однако во время выполнения произойдет сбой с ClassCastException.Разница здесь в том, что averagingInt пытается привести все входящие типы к конкретным, а forEach просто использует типы, что может привести к сбою компиляции.

Это эквивалентно

    ToIntFunction<? super Integer> mapper = new ToIntFunction<Integer>(){
        @Override
        public int applyAsInt(Integer value) {
            return value;
        }
    };

    st.collect(Collectors.averagingInt(mapper));

Анонимный класс будет использоваться внутри averagingInt, и перед передачей аргументов в метод applyAsInt он будет приведен к соответствующему типу (в моем примере к Integer)

Еще одна интересная вещь

Stream st = Stream.of(1,2,3,4);

Consumer<String> stringConsumer = new Consumer<String>() {
    @Override
    public void accept(String o) {

    }
};

st.forEach(stringConsumer); // will compile disregarding to raw type of Stream and specific Consumer

st.forEach((Integer  a) -> System.out.println(a)); // will fail because os raw Stream 
0 голосов
/ 03 февраля 2019

Создает ли это поток целых чисел (путем вывода во время выполнения) или потока объектов?

Общих не существует во время выполнения, во время выполнения нет ничего, что можно "вывести".Поскольку вы использовали необработанный тип для Stream st - это Stream<Object>, если хотите.

Ну, даже то, для чего вы написали (и компилируете):

st.collect(Collectors.averagingInt((Integer x)->x));

не будет компилироваться, когда вы попытаетесь добавить ожидаемый результат:

Double i = st.collect(Collectors.averagingInt((Integer x) -> x));

Интересно, если бы вы переписали, как показано ниже, будет больше смысла:

Collector<Integer, ?, Double> collector = Collectors.averagingInt((Integer x) -> x);
Double result = st.collect(collector); // does not compile

Вывод будет работать для Collectors.averagingInt, но поскольку вы использовали Stream как raw, все остальное тоже raw.

0 голосов
/ 03 февраля 2019

Подписи различны .

Collectors.averagingInt принимает ToIntFunction, из которых возврат тип операции collect связан с типом Stream, который является необработанным.

Stream.forEach принимает Consumer, который напрямую связан с типомStream.

Необработанный тип мешает вашей возможности указать тип в forEach, поэтому вы вызываете ошибку компиляции из-за безопасности типов.

...