Search code examples
javagenericstypechecking

Why can't Java typecheck this code?


I have some stream handling code that takes a stream of words and performs some operations on them, then reduces them to a Map containing the words as keys and the number of occurrences of the word as a Long value. For the sake of the brevity of the code, I used the jOOL library's Seq class, which contains a number of useful shortcut methods.

The code compiles just fine if I write it like this:

item.setWordIndex (
        getWords (item)                      // returns a Seq<String>
              .map (this::removePunctuation) // String -> String
              .map (stemmer::stem)           // String -> String
              .groupBy(str -> str, Collectors.counting ()));

However, if I attempt to replace the str -> str lambda with the more self-documenting Function::identity, I get the following errors:

The method setWordIndex(Map<String,Long>) in the type MyClass is not applicable for the arguments (Map<Object,Long>)
The type Function does not define identity(String) that is applicable here

Why does Function::identity behave any differently to str -> str, which I (perhaps naively) assumed was directly equivalent, and why can't the compiler handle it when it is used?

(And yes, I'm aware I could remove the identity function by moving the previous map application into the groupBy operation, but I find the code clearer like this, because it follows the application logic more directly)


Solution

  • You want Function.identity() (which returns a Function<T, T>), not Function::identity (which matches the SAM type Supplier<Function<T, T>>).

    The following code compiles fine:

    static String removePunctuation(String x) { return x; }
    static String stem(String x) { return x; }
    
    // ...
    
    final Map<String, Long> yeah = Seq.of("a", "b", "c")
            .map(Test::removePunctuation)
            .map(Test::stem)
            .groupBy(Function.identity(), Collectors.counting());