Java 8中返回通用函数接口

4
我想编写一个函数工厂,它应该是一个函数,一次调用并传入不同的策略作为参数。它应该返回一个函数,根据要由谓词满足的参数选择其中之一的策略。 更好地理解,请参考condition3。 问题在于它无法编译。我认为这是因为编译器无法确定可以通过实现来实现函数接口H。 没有泛型时,它可以正常工作。
@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);
    }
}

那么问题是什么?我能解决它吗?如果可以用Java解决,怎么做呢?

请提供完整的编译错误信息。 - Puce
该表达式的目标类型必须是一个函数式接口。它位于方法上方的注释中。 - F. Böller
1个回答

5

请查看您的方法签名,并尝试确定确切的返回类型:

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

Lambda表达式只能实现编译时已知的接口interface。但对于H的实际类型参数,编译器不知道。
您的第一种方法有效,因为它返回Finder<P, Q>类型,这个类型可以由lambda实现;第二种方法有效,因为它不使用lambda实现返回类型H extends Finder<P, Q>
只有第三种方法尝试为类型参数H extends Finder<P, Q>指定lambda表达式。
一种解决方法是不给调用者自由地强制指定Finder的特定子类型作为该方法的返回类型:
static <P, Q, H extends Finder<P, Q>>
    Finder<P, Q> condition3(Pair<Predicate<P>, H>... hs) {

为了说明您原始方法签名的影响,看下面的例子:

举个例子来说明:

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();

鉴于您的原始方法签名,这段代码可以编译通过。但是,在lambda表达式中,如何使用方法condition3来提供HImpl实例呢?请注意,保留HTML标记。

泛型不是在编译时解析的吗?我认为当我使用带有参数的condition3()时,H的类型被定义并且在编译时用具体类型替换了泛型返回类型H。 - F. Böller
1
它们在编译时被解析,但只有方法condition3调用者知道实际的类型参数,因为每个调用点可能使用不同的类型来代替H。与Collections.emptyList()相比:它返回一个List<T>,这个List<T>可能是List<Integer>List<String>,具体取决于你如何使用它。这仅适用于返回的列表为空,因为Collections.emptyList()的实现永远不知道T的类型,并且无法为其提供真实值。 - Holger
但是在 Collections.emptyList() 中没有提供类型参数。我认为应该使用 Pair<Predicate<P>, H>... hs 来定义类型。当我提供一个类型为 Pair<Predicate<PImpl>, HImpl> 的参数时,我希望编译器知道返回类型是 HImpl。由于 HImpl 是因为限制 H extends Finder<P, Q> 而成为函数接口,编译器应该能够找到 lambda 表达式的类型作为 HImpl - F. Böller
1
你似乎对泛型的作用感到困惑。H extends Finder<P, Q>不要求 H 是一个函数式接口。它只是说 H 必须满足 Finder 接口。它可以是任意类型,扩展或实现 Finder。它可以是扩展 Finder 并添加更多方法的接口,因此不是函数式接口。H 的实际类型参数甚至可以是一个final类。请参见我的更新答案。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接