Method references in Java like String::isEmpty can be used easily, but their negations cannot. Read this article and find out how to fix this problem and improve your code.
Method References
As we all know by now, we can use Method References, like
String::isEmpty
, in Java 8 to reference a method that is being used when we, for example, stream over elements. Take a look at this code snippet:
which will produce the result 1 (because there is just one empty element in the stream). But, if we want to filter out non-empty strings, we need to write
.filter(s -> !s.isEmpty())
which is a Lambda. Clearly, there is an annoying asymmetry here. We can use a method reference, but not its negation. We can writepredicate.negate()
but we cannot write Stream::isEmpty.negate()
or !Stream::isEmpty
.
Why is that? It's because a Method Reference is not a Lambda or a Functional Interface. However, a Method Reference can be resolved to one or several Functional Interfaces using Java's type inference. Our example
String::isEmpty
can, in fact, be resolved to at least:Predicate<String>
Function<String, Boolean>
So, we need to somehow resolve all the potential ambiguities and decide which Functional Interface we want to turn the Method Reference into. Read this post and find out how to partially fix this issue. I have used code presented here in the open-source project Speedment that makes databases look like Java 8 Streams. Feel free to try Speedment out.
Speedment also contains predicate builders that allow you to use functions like Entity.NAME::isEmpty and Entity.NAME::isNotEmpty directly.
Resolving Method References
The problem can be partially fixed by introducing some "plumbing" in the form of a static method that takes a Method Reference and returns it as a view of a specific Functional Interface. Consider this short static method:
Now, if we import that method statically we can, in fact, use a Method Reference more easily as shown in this short example:
The code will return 2, which is the number of non-empty elements in the stream. This is a step forward in terms of Method Reference usage. Another benefit is that this solution allows us to compose our predicates more easily, like this:
Resolving All Method References
But there is still a problem we have to resolve. We can not simply start creating a lot of static
as()
functions, because a Method Reference might be resolvable to several types of potential as()
methods in the same way listed in the beginning of this post. So, a better approach is to append the Functional Interface type name to each static method, allowing us to programmatically select a particular Method Reference to Functional Interface conversion method. Here is a utility class that allows Method References to be converted to anymatching Functional Interface that resides in the standard Java package java.util.function
.
Pull down the latest version directly from GitHub here.
So after we have imported the relevant methods statically, we can write:
An Even Better Solution
It would be even better if all the Functional Interfaces themselves contained a static method that could take a suitable Method Reference and turn it into a typed Functional Interface. For example, the standard Java
Predicate
Functional Interface would then look like this:
This would allow us to write:
No comments:
Post a Comment