Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are these Java 8 lambdas behaving differently during type conversions?

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

Does this create a stream of Integers (By inferring at runtime) or a Stream of Objects?

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

This line compiles fine.

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

This line does not compile. It gives a compilation error at (Integer x), saying expected object but found Integer. Why?

Why does it not complain the same for the averagingInt method above?

averagingInt takes the ToIntFunction interface with raw reference type as <? super T> and allowing Integer value. While forEach is taking the Consumer interface with the same raw reference type as <? super T> and the compiler is complaining for the Integer argument value.

like image 528
Khuzi Avatar asked Feb 02 '19 23:02

Khuzi


People also ask

Why are lambdas expression used in Java 8?

In Java 8, using lambda expression and Stream API we can pass processing logic of elements into methods provided by collections and now collection is responsible for parallel processing of elements and not the client. Also, parallel processing effectively utilizes multicore CPUs used nowadays.

What is the type of lambda expression in Java 8?

Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.

Which of these statements are true for lambda expressions in Java 8?

Q 5 - Which of the following is correct about Java 8 lambda expression? A - Lambda expressions are used primarily to define inline implementation of a functional interface.

Is lambda expressions are always assigned to a functional interface in Java 8?

From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. A functional interface can have any number of default methods. Runnable, ActionListener, Comparable are some of the examples of functional interfaces.


2 Answers

Does this create stream of Integers (By inferring at runtime) or Stream of Objects ?

Generics do not exist at runtime, there is nothing to "infer" at runtime. Since you have used a raw type for the Stream st - it is a Stream<Object> if you want.

Well, even what you wrote for (and does compile):

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

would not compile when you try to add the expected result:

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

I wonder if you re-write like below would make more sense:

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

Inference would work for Collectors.averagingInt, but since you have used Stream as raw - everything else is raw too.

like image 176
Eugene Avatar answered Oct 05 '22 02:10

Eugene


As was told above you are trying to use raw Stream. If you want to iterate across Integer then you need to specify generic for Stream.

For instance

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

Then it will compile.

This probalem is equuvalent to problem with iterating across generic colection in old style

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

The code above won't compile because values is list of objects not integers.

As for averagingInt it will compile even if you will provide a list of String. For instance

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

However it will failed during runtime with ClassCastException. The difference here is that averagingInt tryes to cast all incoming types to specific and forEach just uses types as is what may cause compilation fail.

It is equivalent

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

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

The anonymous class will be used inside of averagingInt and before passing args to applyAsInt method it will be cast to appropriate type (in my sample to Integer )

Another interesting thing

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 
like image 34
mger1979 Avatar answered Oct 05 '22 01:10

mger1979