简体   繁体   中英

Can someone explain to me this method signature

I am looking at the following code (someone else's code) and part of the code seems obscure to me:

List<string> result = forkJoinPool.invokeAll(tasks)
            .stream()
            .map(MUtil.rethrowFunction(Future::get))
            .collect(toList());

This part is straightforward, a ForkJoinPool.invokeAll() returning a List of Future objects and further processing returns a List of Strings.

Later, the list.stream().map() uses a static method on Mutil class Mutil.rethrowFunction(Future::get) and passes it an object of Future type. Looking at the Mutil source code:

public class Mutil {
    public static <T, R> Function<T, R> rethrowFunction(WithExceptionsIF<T, R> function) {
        return t -> {
            try { return function.apply(t); }
            catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }
    @FunctionalInterface
      public interface WithExceptionsIF<T, R> {
          R apply(T t) throws Exception;
    }

    @SuppressWarnings ("unchecked")
    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}

Here are my questions, as I am learning Generics, lambda and java.util.function package:
  1. The signature of Mutil.rethrowFunction() , says it returns <T, R> Function<T, R> and uses a parameter of type WithExceptionsIF<T, R> (which is a functional interface). How does Future.get() translates into the Mutil.rethroFunction() signature? Future.get(), returns a computed result and not a Function?

  2. How does Future.get() , which is passed to Mutil.rethrowFunction() translates to WithExceptionIF<T,R> type?

  3. t->{return function.apply(t);} what is going on in this statement? is 't' the Future object, if so then who is the "function"?

  4. The signature of Mutil.throwAsUnchecked(Exception exception) method has <E extends Throwable> defined after the keyword "static". If "exception" is the only parameter passed to the method, where does the E come from and why is it declared before the method's return type (void)?

Thanks for any clarity.

Here's a complete runnable example.

package org.example;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.function.Function;

import static java.util.stream.Collectors.toList;

public class SO62737345 {
    public static void main(String... args) {
        ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
        List<Callable<String>> tasks = Collections.singletonList(() -> "fish");
        List<String> result = forkJoinPool.invokeAll(tasks)
                .stream()
                .map(rethrowFunction(Future::get))
                .collect(toList());
    }

    public static <T, R> Function<T, R> rethrowFunction(WithExceptionsIF<T, R> function) {
        return t -> {
            try {
                return function.apply(t);
            }
            catch (Exception exception) { throwAsUnchecked(exception);  return null; }
        };
    }
    @FunctionalInterface
    public interface WithExceptionsIF<T, R> {
        R apply(T t) throws Exception;
    }

    @SuppressWarnings ("unchecked")
    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}
  1. The get() method doesn't execute when rethrowFunction is called. The call to rethrowFunction just transforms the function passed in from one which throws a checked exception to one which doesn't (because any exception inwrapped in an unchecked exception). The collect() method actually calls the function, once for each Future in the Stream.

  2. Think about passing Future::get using a lambda rather than a method reference. It would be f -> f.get() . because we are passing a member function it takes the object it belongs to as a parameter. SO we are passing a function, and as the signature of get() is V get() throws InterruptedException, ExecutionException it matches WithExceptionsIF . In fact it is an instance of WithExceptionsIF<Future,String> in this particular case.

  3. If you expand this by substituting the lambda above, you get: return (f -> f.get()).apply(t) so get is being applied to the parameter being passed when the function which rethrowFunction` returns is evaluated.

  4. This is explained here A peculiar feature of exception type inference in Java 8 Java is performing 'type inference'. The compiler has been told that throwAsUnchecked throws an object whose class is a subclass of Throwable. Because the return type of rethrowFunction does not declare a checked exception, the compiler decides that throwAsUnchecked must be throwing a RuntimeException . Another way of looking at it is that the type parameter in this case has its value inferred from the context the function is invoked in, rather than from its parameters. At least I think that's how it works, it is not that easy to understand!

Explaining that generic syntax more generally, often a method is part of a parameterised type, eg List<A> , so the type parameter can appear in the signature of member functions, eg A get(int i) . You can also have a generic function whose signature has type parameters which do not appear in the class (or a static function which is not associated with an instance of the class), and here they are declared before the return type.

The get() method doesn't execute when rethrowFunction is called …“

That's incorrect. Future::get is too called in rethrowFunction()s call chain; as a side effect of rethrowFunction() being called. The return type of Future::get is a MUtil.WithExceptionsIF . It is that return value that is passed as the input argument to rethrowFunction() .

That just transforms the function passed in from one which throws a checked exception to one which doesn't…

The wording of this sentence is confusing. It reads as if the „ That “ in the sentence refers to the Future::get . The get() method doesn't do that transform . The rethrowFunction() method is what does the heavy lifting.

Think about passing Future::get…because we are passing a member function it takes the object it belongs to as a parameter

Future.get() doesn't take any parameters .

…as the signature of get() is V get() throws InterruptedException, ExecutionException it matches WithExceptionsIF in this particular case …“

This is not correct either. The signature of Future.get() has nothing to do with anything. It's certainly not true that the signature of Future.get() matches WithExceptionsIF . Again, the return type of Future.get() would be WithExceptionsIF in this particular invocation. But a method's return type is not part of its signature .

Future.get() translates to * WithExceptionsIF<T,R> because forkJoinPool.invokeAll(tasks) returns a List<Future <MUtil.WithExceptionsIF<T,R>>> .

If you expand this by substituting the lambda above, you get: return (f -> f.get()).apply(t) so get is being applied to the parameter being passed when the function which rethrowFunction` returns is evaluated.

Incorrect. Again, Future.get() has nothing to do with it. Future.get()s job was done after it passed the WithExceptionsIF into rethrowFunction . The t -> {...} block defines a lambda which is returned by the call to rethrowFunction() . The t is the input argument to the lambda. Its type will be of whatever type the type parameter T gets instantiated as.

Some concrete implementation of the WithExceptionsIF.apply() method is what would be called when that lambda is actually invoked; at some unknown later point.

The function we see there is an instance of WithExceptionsIF<T, R> passed in to rethrowFunction(WithExceptionsIF<T, R> function) .

…If "exception" is the only parameter passed to the method, where does the E come from and why is it declared before the method's return type (void)?

This: static <E extends Throwable> void throwAsUnchecked(Exception exception) is how generic methods are declared . The E type variable declared in the type parameter section is used in the method's throws clause. Also in its body; to cast the Exception that's passed in to the method, to be of another, more specific type that would be specified at the point the generic method is actually invoked.

Last, but not least: the List<string>... assignment is wrong for a couple reasons. The main reason is the stream in your example does not produce Stream<String> . It produces Stream<Function<T, R>> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM