What use is a method that just returns its input? Surprisingly useful. A surprising use is as a way to convert between types.
There’s a well known trick that’s often used to work around Java’s terrible array literals that you may have come across. If you have a method that takes an array as an argument
public static void foo(String[] anArray) { }
Invoking foo requires the ugly
foo(new String[]{"hello", "world"});
For some reason Java requires the redundant “new String[]” here, even though it can be trivially inferred. Fortunately we can work around this with the following method which at first glance might seem pointless.
public static T[] array(T... input) {
return input;
}
It just returns the input. However it is accepting an array of Type T in the form of a varargs, and returning that array. It becomes useful due as now Java will infer the types and create the array cleanly. We can now call foo like so.
foo(array("hello", "world"));
That is a neat trick, but it really becomes useful in Java 8 thanks to structural typing of lambdas/method references and implicit conversions between types. Here’s another example of a method that’s far more useful than it appears at first glance. It just accepts a function and returns the same function.
public static Function f(Function f) {
return f;
}
The reason it’s useful is we can pass it any structurally equivalent method reference and have it converted to a java.util.function.Function, which provides us some useful utility methods for function composition.
Here’s an example. Let’s say we have a list of Libraries (Collection of Collection of Books)
interface Library {
List books();
static Library library(Book... books) {
return () -> asList(books);
}
}
interface Book {
String name();
static Book book(String name) { return () -> name; }
}
List libraries = asList(
library(book("The Hobbit"), book("LoTR")),
library(book("Build Quality In"), book("Lean Enterprise"))
);
We can now print out the book titles thusly
libraries.stream()
.flatMap(library -> library.books().stream()) // Stream of libraries to stream of books.
.map(Book::name) // Stream of names
.forEach(System.out::println);
But that flatMap call is upsetting, everything else is using a method reference, not a lambda expression. I’d really like to write the following, but it won’t compile because flatMap requires a function that returns a Stream, rather than a function that returns a List.
libraries.stream()
.flatMap(Library::books) // Compile Error, wrong return type.
.map(Book::name)
.forEach(System.out::println);
Here’s where our method that returns its input comes in again. This compiles fine.
libraries.stream()
.flatMap(f(Library::books).andThen(Collection::stream))
.map(Book::name)
.forEach(System.out::println);
public static Function f(Function f) {
return f;
}
This works because Library::books is equivalent to a Function<Library, List<Book>>, so passing it to the f() method implicitly converts it to that type. java.util.function.Function provides an andThen method which returns a new function which composes the two functions.
Now in this trivial example it’s actually longer to write this than the equivalent lambda, but it can be useful when combining more complex examples.
We can do the same thing with other functional interfaces. For example to allow Predicate composition or negation.
Here we have a handy isChild() method implemented for us on Person, but we really want the inverse – isAdult() check to pass to the serveAlcohol method. This sort of thing comes up all the time.
interface Person {
boolean isChild();
static Person child() { return () -> true; }
static Person adult() { return () -> false; }
}
public static void serveAlcohol(Person person, Predicate isAdult) {
if (isAdult.test(person)) System.out.println("Serving alcohol");
}
If we want to reuse Person::isChild we can do the same trick. The p() method converts the method reference to a Predicate for us, and we can then easily negate it.
serveAlcohol(adult(), p(Person::isChild).negate());
public static Predicate p(Predicate p) {
return p;
}
Have you got any other good examples?