Java8 method reference used as Function object to combine functions

前端 未结 7 1582
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-14 15:02

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         


        
相关标签:
7条回答
  • 2020-12-14 15:29

    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;     
        }
    }
    
    0 讨论(0)
  • 2020-12-14 15:29

    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));
    
    0 讨论(0)
  • 2020-12-14 15:30

    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
    
    0 讨论(0)
  • 2020-12-14 15:31

    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.

    0 讨论(0)
  • 2020-12-14 15:40

    You could write:

    Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
    
    0 讨论(0)
  • 2020-12-14 15:41

    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.

    0 讨论(0)
提交回复
热议问题