简体   繁体   中英

Return generic functional interface in Java 8

I want to write kind of function factory. It should be a function, which is called once which different strategies as parameters. It should return a function, which selects one of this strategies dependent on the parameter, which is to be fulfilled by a predicate. Well, better look at condition3 for better understanding. The problem is, that it is not compiling. I think because the compiler can't figure out, that the functional interface H can be realized by the implementation. Without generics it is working fine.

@FunctionalInterface
public interface Finder<T, S> {

    Stream<S> findBest (T t);

    // Valid code
    static <P, Q> Finder<P, Q> condition1 () {
        return p -> null;
    }

    // Valid code, but selects just one of the H's when the method is invoked
    static <P, Q, H extends Finder<P, Q>> H condition2 (Pair<Predicate<P>, H>... hs) {
        return hs[0].getRight ();
    }

    // Should return a method, which selects the appropiate H 
    // whenever it is invoked with an P
    // Compiler complain: 
    // The target type of this expression must be a functional interface
    static <P, Q, H extends Finder<P, Q>> H condition3 (Pair<Predicate<P>, H>... hs) {
        return p -> stream (hs).filter (pair -> pair.getLeft ().test (p))
                               .findFirst ()
                               .map (Pair::getRight)
                               .map (h -> h.findBest (p))
                               .orElseGet (Stream::empty);
    }
}

So what's the problem here? Can I solve it and if it's possible with Java: how?

Look at the signature of your method and try to tell what the exact return type is:

static <P, Q, H extends Finder<P, Q>> H condition3(…

Lambdas can only implement an interface known at compile-time. But the actual type argument for H is not known to the compiler.

Your first method works because it returns the type Finder<P, Q> which the lambda can implement, your second works because it doesn't use a lambda for implementing the return type H extends Finder<P, Q> .

Only your third method attempts to specify a lambda expression for a type argument H extends Finder<P, Q> .


A solution is not to give the caller the freedom to mandate a particular sub-type of Finder as the method's return type:

static <P, Q, H extends Finder<P, Q>>
    Finder<P, Q> condition3(Pair<Predicate<P>, H>... hs) {

To illustrate what implications your original method signature has, look at the following example:

final class HImpl implements Finder<String,String> {
    public Stream<String> findBest(String t) {
        return null; // just for illustration, we never really use the class
    }
}

HImpl x=Finder.<String,String,HImpl>condition3();

Given your original method signature this compiles without any error. But how ought the method condition3 provide an instance of HImpl here using your lambda expression?

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