Why does this java 8 stream operation evaluate to Object instead of List<Object> or just List?

后端 未结 2 1527
伪装坚强ぢ
伪装坚强ぢ 2020-11-28 12:32

I\'m working with a 3d party library, and they return Collections that lack type specifications (e.g. public List getFoo();) , and I\'m trying to convert their

相关标签:
2条回答
  • 2020-11-28 13:13

    The stream(), map(), collect(), and all the other stream methods rely on generic typing for things to work well. If you use a raw type as input, then all the generics are erased, and everything turns into a raw type. For example, if you have a List<String>, calling the stream() method gives you an object of type Stream<String>.

    But if you start out with a raw type List, then calling stream() gives you an object of the raw type Stream instead.

    The map() method has this declaration:

        <R> Stream<R> map(Function<? super T,? extends R> mapper)
    

    but since it's called on a raw Stream, it's as if the declaration is

        Stream map(Function mapper)
    

    so the result of calling map() is simply a Stream. The signature of collect() is filled with generics:

        <R> R collect(Supplier<R> supplier,
                      BiConsumer<R,? super T> accumulator,
                      BiConsumer<R,R> combiner)
    

    When called with a raw type, it gets erased to

        Object collect(Supplier supplier,
                       BiConsumer accumulator,
                       BiConsumer combiner)
    

    so the return type of your stream pipeline is Object when its source is a raw type. Obviously this isn't compatible with ArrayList<String> and you get a compilation error.

    To fix this, bring your types into the generic world as soon as possible. This will probably involve unchecked casts, and you should probably suppress the warning. Of course, do this only if you're sure the returned collection contains objects only of the expected type.

    In addition, as Hank D pointed out, the toList() collector returns a List, not an ArrayList. You can either assign the return value to a variable of type List, or you can use toCollection() to create an instance of ArrayList explicitly.

    Here's the code with these modifications included:

        @SuppressWarnings("unchecked")
        ArrayList<Integer> l = (ArrayList<Integer>)getRawArrayList();
        l.add(1);
        l.add(2);
        ArrayList<String> l2 = l.stream()
                                .map(Object::toString)
                                .collect(Collectors.toCollection(ArrayList::new));
    
    0 讨论(0)
  • 2020-11-28 13:23

    Collectors.toList() returns a List, not an ArrayList. This will compile:

    public static void main(String[] args) {
        ArrayList<?> l = getRawArrayList();
        List<String> l2 = l.stream().map(Object::toString).collect(Collectors.toList());
    
    }
    
    0 讨论(0)
提交回复
热议问题