在不失去通用性的情况下包装逆变功能界面

Hen*_*olm 5 java generics nested-generics

我有一些具有这种通用结构的代码:

interface Func<A> {
    double apply(Optional<A> a);
}

class Foo {
    public double compute(Func<Double> f) {
        // Sometimes amend the function to do something slightly different
        Func<Double> g = f;
        if (someCondition())
            g = oa -> Math.max(0, f.apply(oa));

        return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());
    }
}
Run Code Online (Sandbox Code Playgroud)

这本身就足够了.现在我想变得更自由,以至于如果某人拥有,Func<Number>或者Func<Object>代替Func<Double>他们,他们仍然可以将其传递进去compute.先验,这应该是足够安全的.很好,所以我把它改成了

    public double compute(Func<? super Double> f) {
        Func<? super Double> g = f;
        if (someCondition())
            g = oa -> Math.max(0, f.apply(oa));
        ...
Run Code Online (Sandbox Code Playgroud)

不幸的是现在lambda没有进行类型检查(说Eclipse),因为oa无法传递给它f.apply.

的分配f本身g似乎并不担心编译器,并且不会出现这个问题,如果该参数applyA而非Optional<A>.

不幸的是,似乎没有成为一个方式来命名? super Double参数类型,所以我可以再次使用它的类型g和/或用老式的内部类更换拉姆达.

例如,这甚至在语法上都不允许:

    public <T super Double> double compute(Func<T> f) {
        Func<T> g = f;
        ...
Run Code Online (Sandbox Code Playgroud)

是否有任何相当优雅的方式来完成这项工作?

到目前为止,我提出的最好的是

    public double compute(Func<? super Double> f) {
        if (someCondition())
            return computeInner(oa -> Math.max(0, f.apply(oa)));
        else
            return computeInner(f);
    }

    private double computeInner(Func<? super Double> g) {
        return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());
    }
Run Code Online (Sandbox Code Playgroud)

编译器接受了这一点,但间接调用除了使类型检查器满意之外别无其他原因并不是我称之为优雅的.

And*_*ner 2

试试这个奇怪的技巧:

g = oa -> Math.max(0, f.apply(oa.map(a -> a)));
                             // ^----------^
Run Code Online (Sandbox Code Playgroud)

像这样映射可选类型允许编译器将可选类型“转换”为一致的类型。

Ideone demo

这确实有创建新实例的缺点Optional


但是,当然,我问了这个问题,思考这是否实际上是规范所允许的,或者是一个错误。


就我个人而言,我并不认为你的“迄今为止最好的”特别令人震惊。当然,这取决于真正的代码看起来如何。