4

I have some code that looks a bit like this (it's simplified here):

public Optional<String> parseInput(String input){
    Pattern pattern = Pattern.compile("([A-Za-z]+)([0-9]{2}|[0-9]{4})");
    Matcher matcher = pattern.matcher(input);
    return Optional.of(matcher.find())
                   .filter(t -> t) // .filter(Function.identity())
                   .map(ignore -> matcher.group(1));
}

But it fails with an error:

Error: incompatible types: no instance(s) of type variable(s) T exist so that java.util.function.Function<T,T> conforms to java.util.function.Predicate<? super java.lang.Boolean>

What is going on here, and why is it designed this way?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • 5
    `filter` expects a predicate that returns a boolean. `Function.identity()` doesn't do that. Don't think it works in the `Stream` filter either – Robin Topper May 04 '17 at 14:35
  • I didn't actually check that it works with `Stream`, but I made the assumption after coming across [this question](http://stackoverflow.com/questions/28032827/java-8-lambdas-function-identity-or-t-t) while trying to find an answer. – Peter Hall May 04 '17 at 14:40
  • 5
    `Functon.identity()` returns as object of type `Function`, but `filter` wants an object of type `Predicate` – 4castle May 04 '17 at 14:41
  • Right, I see now. Java won't let you define `Predicate` to be an alias for `Function`. – Peter Hall May 04 '17 at 14:44
  • 5
    You could use `Boolean::booleanValue` – 4castle May 04 '17 at 14:44
  • 1
    Had to test it .... It also doesn't work in a `Stream` filter. Not even with a `Stream`. As you said, Java won't let you use a Function as a Predicate – Robin Topper May 04 '17 at 14:47
  • @RobinTopper I have tested it and you are right. I'm now confused about this question: [Java 8 lambdas, Function.identity() or t->t](http://stackoverflow.com/questions/28032827/java-8-lambdas-function-identity-or-t-t) – Peter Hall May 04 '17 at 14:48
  • 2
    It is used in various map operators in that question and the answers. And those do take a Function. – Robin Topper May 04 '17 at 14:50
  • Yep. That makes sense. `filter` is more constrained than `map`. – Peter Hall May 04 '17 at 14:51

2 Answers2

6

The main issue here is in an incorrect type. A Predicate is not a Function, it is not a subinterface of a Function, but rather an independent functional interface. Stream#filter accepts a Predicate as parameter: filter(Predicate<? super T> predicate). Since there is no relation between Predicate and Function, we can't use Function with filter.

What might be confusing in this case, is that even if t -> t looks exactly like Function.identity() or just Function, it might not be a Function. An important thing to remember about lambda expressions is that a lambda itself doesn't contain any information about its type. That information is deduced from the context. The same lambda can represent different functional interfaces.

Predicate<Boolean> predicate = t -> t;
Function<Boolean,Boolean> function = t -> t;

Predicate<Boolean> predicateBoolean = Boolean::booleanValue;
Function<Boolean,Boolean> functionBoolean = Boolean::booleanValue;
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Anton Balaniuc
  • 10,889
  • 1
  • 35
  • 53
4

Summarising the comments, I can answer my own question.

Predicate<T> is unrelated to Function<T, Boolean>, and they can't be used in each other's place, even if it would often appear to make logical sense.

Predicate<T> likely exists because there are times when you might want to compose predicates using the methods: and, or and negate, and these methods just wouldn't makes sense on Function<T, U>. Java does not permit methods to be defined only for a subset of the possible generic parameters, so predicates are more ergonomically modeled by Predicate<T> rather than Function<T, Boolean> for common use-cases.

Another reason for having a separate interface for predicates is that Java does not permit primitives in generic parameters. That means you cannot define a predicate as Function<T, boolean> — it would have to be Function<T, Boolean>, so a caller of the predicate would have to handle the possibility of null return values. Using a specialized interface allows the return value of test to be a boolean instead and allows callers to assume the result is never null .

A possible replacement in my original use case is Boolean::booleanValue, which can be used where either Predicate<Boolean> or Function<Boolean, Boolean> is required.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • 1
    There is another advantage of `Predicate` over `Function` for the intended use case: the caller of `Predicate.test` does not have to deal with `null` results. – Holger May 04 '17 at 16:21
  • @Holger Good observation! I'll add that. – Peter Hall May 04 '17 at 16:24