如何递归引用泛型参数?

9

我已经解决了一个Y组合器的问题。刚刚我发现我不能递归地引用一个泛型参数。

Y = λf.(λx.f (x x)) (λx.f (x x))

例如:
IntUnaryOperator fact = Y(rec -> n -> n == 0 ? 1 : n * rec.applyAsInt(n - 1));

IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return g(g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

IntUnaryOperator g(G g) {
    return g.apply(g);
}

//        v--- I want to remove the middle-interface `G`
interface G extends Function<G, IntUnaryOperator> {/**/}

问题:如何在方法g上使用泛型参数,避免引入额外的接口G,并且泛型参数应该避免UNCHECKED警告?

谢谢。


2
我非常确定这是不可能的。据我所知,Java没有提供一种方法来要求给定的泛型参数是一个函数式接口。 - Ry-
1个回答

4
你可以使用递归类型定义声明一个泛型方法。
<G extends Function<G, IntUnaryOperator>> IntUnaryOperator g(G g) {
    return g.apply(g);
}

无法通过使用lambda表达式调用该方法并将其分配给G来实现。规范说:
15.27.3. Lambda表达式的类型

如果T是函数接口类型(§9.8)…,则在赋值上下文、调用上下文或强制转换上下文中,lambda表达式与目标类型T兼容。

而G不是函数接口,而是类型参数,在这里无法推断出实际的接口类型。
当你使用实际接口G作为lambda表达式时,仍然可以工作:
IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return g((G)g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

// renamed the type parameter from G to F to avoid confusion
<F extends Function<F, IntUnaryOperator>> IntUnaryOperator g(F f) {
    return f.apply(f);
}

// can't get rid of this interface
interface G extends Function<G, IntUnaryOperator> {/**/}

或者

IntUnaryOperator fact = Y(rec -> n -> n == 0 ? 1 : n * rec.applyAsInt(n - 1));

IntUnaryOperator Y(Function<IntUnaryOperator, IntUnaryOperator> f) {
    return this.<G>g(g -> f.apply(x -> g.apply(g).applyAsInt(x)));
}

// renamed the type parameter from G to F to avoid confusion
<F extends Function<F, IntUnaryOperator>> IntUnaryOperator g(F f) {
    return f.apply(f);
}

// can't get rid of this interface
interface G extends Function<G, IntUnaryOperator> {/**/}

因此,方法g是通用的,不依赖于接口G,但仍需要将该接口用作lambda表达式的目标类型。

是的,我刚发现类型变量也可以递归引用,但问题出现在Lambda表达式中。非常感谢您,先生。例如:g(null)可以正常工作,但g(g - > null)无法正常工作。 - holi-java

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