Is there a way in Java8 to use a method reference as a Function
object to use its methods, something like:
Stream.of("ciao", "hola", "hello")
.map(String::length.andThen(n -> n * 2))
This question is not related to the Stream
, it is used just as example, I would like to have answer about the method reference
You can write a static method to do this:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = combine(String::length, n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2, T3> Function<T1, T3> combine(
Function<T1, T2> first,
Function<T2, T3> second) {
return first.andThen(second);
}
}
You could then put it in a utility class and import it statically.
Alternatively, create a simpler static method which just returns the function it's given, for the sake of the compiler knowing what you're doing:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = asFunction(String::length).andThen(n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2> Function<T1, T2> asFunction(Function<T1, T2> function) {
return function;
}
}
You can just save it into a variable:
Function<String, Integer> toLength = String::length;
Stream.of("ciao", "hola", "hello")
.map(toLength.andThen(n -> n * 2));
Or you can use a cast, but it's less readable, IMO:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2));
You should be able to achieve what you want inline by using casts:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2))
There are only 'type hints' for the compiler, so they don't actually 'cast' the object and don't have the overhead of an actual cast.
Alternatively, you can use a local variable for readability:
Function<String, Integer> fun = String::length
Stream.of("ciao", "hola", "hello")
.map(fun.andThen(n -> n * 2));
A third way that may be more concise is with a utility method:
public static <T, X, U> Function<T, U> chain(Function<T, X> fun1, Function<X, U> fun2)
{
return fun1.andThen(fun2);
}
Stream.of("ciao", "hola", "hello")
.map(chain(String::length, n -> n * 2));
Please note that this is not tested, thus I don't know if type inference works correctly in this case.
You may also use
Function.identity().andThen(String::length).andThen(n -> n * 2)
The problem is, String::length
is not necessarily a Function
; it can conform to many functional interfaces. It must be used in a context that provides target type, and the context could be - assignment, method invocation, casting.
If Function
could provide a static method just for the sake of target typing, we could do
Function.by(String::length).andThen(n->n*2)
static <T, R> Function<T, R> by(Function<T, R> f){ return f; }
For example, I use this technique in a functional interface
static <T> AsyncIterator<T> by(AsyncIterator<T> asyncIterator)
Syntax sugar to create an AsyncIterator from a lambda expression or a method reference.
This method simply returns the argument
asyncIterator
, which seems a little odd. Explanation:Since AsyncIterator is a functional interface, an instance can be created by a lambda expression or a method reference, in 3 contexts:
// Assignment Context
AsyncIterator<ByteBuffer> asyncIter = source::read;
asyncIter.forEach(...);
// Casting Context
((AsyncIterator<ByteBuffer>)source::read)
.forEach(...);
// Invocation Context
AsyncIterator.by(source::read)
.forEach(...);
The 3rd option looks better than the other two, and that's the purpose of this method.
You can use a cast
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length)
.andThen(n -> n * 2))
.forEach(System.out::println);
prints
8
8
10
You could write:
Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
来源:https://stackoverflow.com/questions/32206092/java8-method-reference-used-as-function-object-to-combine-functions