15

When a Java method accepts a Function<? super T, ? extends U>, then it's possible to provide method references in a syntax like the following: MyClass::myMethod.

However, I'm wondering if there's a way to chain multiple method calls. Here's an example to illustrate the case.

// on a specific object, without lambda
myString.trim().toUpperCase()

I am wondering if there is a syntax to translate this to a lambda expression. I am hoping there is something like the following:

// something like: (which doesn't work)
String::trim::toUpperCase

Alternatively, is there a utility class to merge functions?

// example: (which does not exist)
FunctionUtil.chain(String::trim, String::toUpperCase);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
bvdb
  • 22,839
  • 10
  • 110
  • 123
  • How about [`Function.andThen`](https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#andThen-java.util.function.Function-)? – Jeff Bowman Sep 09 '15 at 21:22
  • 2
    The canonical solution is to _stop using method references_, and just write `myString -> myString.trim().toUpperCase()`. – Louis Wasserman Sep 09 '15 at 21:32
  • @LouisWasserman an excellent workaround. But I was really hoping for more syntactic sweetness. – bvdb Sep 09 '15 at 21:42
  • 3
    To be honest, all the available "syntactically sweet" options are actually uglier, frankly. Storing the lambdas in variables, writing a special static method, etc. are all more of a pain than just doing the straightforward thing. – Louis Wasserman Sep 09 '15 at 21:43
  • That's an opinion. But then again, in my opinion, you are wrong on this one. But that's also just an opinion. - My view: I regard the `::` as an **operator**, or maybe like a dot (`.`). If all other operators are chainable/combinable then why isn't this one? It's strange that the `::` can only be used once in an expression. As far as I know, that makes it the most limitted operator ever. (lacks flexibility) - Then again, just an opinion. - But I do agree on this: If it's so limitted, then they shouldn't have introduced it in the first place, then better just **always** use the `->` syntax. – bvdb Sep 09 '15 at 21:50
  • 2
    Well it's kind of like 'function pointer' in C. So that we can refer to a function by its name. It makes no sense to "chain" that operation. Dot(.) are not chained, they are nested - `a.f().g() == (a.f()).g()`. We can nest `::` too:) – ZhongYu Sep 09 '15 at 22:16
  • One more ugly version: `Stream.>of(String::trim, String::toUpperCase).reduce(Function.identity(), Function::andThen)` – Tagir Valeev Sep 10 '15 at 01:58
  • 1
    @bvdb: You *can* combine the `::` operator. Since `x::y` *is* an expression, the form `(x::y)::z` denote the form *expression::name*. Unfortunately, the left-hand side has no type, so it doesn’t work, but if it had a type, it was a *function* type as the expression `x::y` produces a *function*. Thus, `z` would be searched within the function type which is not what you want as you want to have `z` be searched within the result type. But there is no point in changing the way expression nesting works, as there is already a way to denote what you want: `x->y().z()`. – Holger Sep 10 '15 at 09:29
  • See also http://stackoverflow.com/q/29146199/2711488 or http://stackoverflow.com/q/27559960/2711488 – Holger Sep 10 '15 at 09:29
  • @Holger So, I understand now that `(x::y)` isn't always a `java.util.function.Function`. I guess that it can be casted to any interface with 1 abstract method (*or is annotation `@FunctionalInterface` a requirement (?)*). - It's unfortunate that the compiler cannot determine it automatically. Briefness was the goal after all. – bvdb Sep 10 '15 at 13:42
  • 1
    You can cast it to any single-abstract-method interface, but as said, that would imply searching the second method within that interface, not within the return type. So `((Function)x::y)::apply` would be valid, but not useful. It does not create a function chain. It would be useful if `Function` had a `static` method, say `chain`, accepting two `Function`s so that you could write `Function.chain(x::y, r::z)` whereas `r` is the result type of `y()`. However, it’s still no advantage over `x->x.y().z()`… – Holger Sep 10 '15 at 14:04

1 Answers1

9

Java 8 Functions can be chained with the method andThen:

UnaryOperator<String> trimFunction = String::trim;
UnaryOperator<String> toUpperCaseFunction = String::toUpperCase;
Stream.of(" a ", " b ").map(trimFunction.andThen(toUpperCaseFunction)) // Stream is now ["A", "B"]

Note that in your actual example, String::trim does not compile because the trim method does not take any input, so it does not conform to the functional interface Function (same goes for String::toUpperCase).

Tunaki
  • 132,869
  • 46
  • 340
  • 423