Java 8 Streams:Why does mapToInt needs the Integer::parseInt as a parameter?

dushkin :

I am trying to understand something following the next example:

    Stream.of("a1", "a2", "a3")
            .map(s -> s.substring(1))
            .mapToInt(Integer::parseInt)
            .max()

     . (etc.)

Why does mapToInt needs the

    Integer::parseInt 

as a parameter? Shouldn't it be implicit for it? Isn't this parameter redundant?

Thanks!

Slaw :

It's important to differentiate between what calling Stream#mapToInt(ToIntFunction) does and what the ToIntFunction argument does.

  • The call to mapToInt is what the stream is going to do (i.e. map the elements to an int).
  • The ToIntFunction argument is how the stream is going to map each element to an int.

Could they have included a no-arg mapToInt method that implicitly parses Strings to ints? Yes, but look at how well that works for Stream#sorted()—and that situation is nowhere near as arbitrary or ambiguous as a no-arg mapToInt method.

The no-arg sorted method assumes the elements are Comparable which is a fundamental, standardized and wide-spread interface in Java—any class can implement the interface (whereas there's only one class that can be a String). Thus while the method is not type-safe it can be argued the use-case is common enough to justify its existence.

However, a no-arg mapToInt method that assumes the elements are Strings and parses their content to an int is a highly specific use-case. Why would such a generic API offer such a specific operation? It'd be an entirely arbitrary decision with no reasonable justification. For instance, why not map each String to its length instead? And why is String being handled specially but not some other type(s)? Hence the method accepts a ToIntFunction argument that describes how to map the elements to an int on a case-by-case basis.


It may help to note that Integer::parseInt is a method reference, which is shorthand for a trivial lambda expression. Both are an implementation of a functional interface which is less verbose option than using an anonymous class. In other words, you're creating a new ToIntFunction instance and passing it as an argument.

All of the following are functionally equivalent:

// method reference
stream.mapToInt(Integer::parseInt)

// lambda expression
stream.mapToInt((String value) -> Integer.parseInt(value)) // explicit parameter types
stream.mapToInt(value -> Integer.parseInt(value))          // implicit parameter types

// anonymous class
stream.mapToInt(new ToIntFunction<>() {

    @Override
    public int applyAsInt(String value) {
        return Integer.parseInt(value);
    }

})

Where stream is a Stream<String>.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=128014&siteId=1