猿问

包装一个互逆的功能接口而不会失去通用性

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


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

    }

}

就其本身而言,这已经足够好了。现在,我想更加自由一些,这样,如果有人拥有aFunc<Number>或Func<Object>代替a的话,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));

        ...

不幸的是,现在lambda不会进行类型检查(例如Eclipse),因为oa无法将类型传递给f.apply。


f本身的赋值g似乎并不使编译器感到担心,并且如果to的参数apply为A而不是,也不会出现问题Optional<A>。


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


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


    public <T super Double> double compute(Func<T> f) {

        Func<T> g = f;

        ...

有什么合理的优雅方法可以完成这项工作吗?


到目前为止,我想出的最好的方法是


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

    }

编译器接受了这一点,但是除了使类型检查器满意之外,间接调用实际上并不是我所说的“优雅”。


DIEA
浏览 112回答 3
3回答

慕田峪7331174

试试这个奇怪的把戏:g&nbsp;=&nbsp;oa&nbsp;->&nbsp;Math.max(0,&nbsp;f.apply(oa.map(a&nbsp;->&nbsp;a))); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;^----------^像这样映射可选类型的类型,可使编译器将“可选”的类型“转换”为一致的类型。Ideone demo这确实存在创建新Optional实例的缺点。但是,当然,我问了一个问题,它在思考这是否实际上是规范所允许的东西或错误。就个人而言,我认为您的“迄今为止最好的”并不特别糟糕。当然,这取决于实际代码的外观。

翻阅古今

总的来说,我发现Java中超界的类型推断是一个噩梦。.有很多编译器错误,通常很难推理为什么编译器会拒绝某种语法而不拒绝其他语法。就是说,您可以通过强制转换f为Func<Double>类型来解决这种问题。这并不是完全安全的方法。如果f具有任何状态,我们可以假设其下限f是Double在现实中可能是Number或Object。使用强制转换,您不会招致@AndyTurner提供的解决方案的性能损失,但是未经检查的强制转换也不是您的朋友。public double compute(Func<? super Double> f) {&nbsp; &nbsp; // Sometimes amend the function to do something slightly different&nbsp; &nbsp; Func<? super Double> g = f;&nbsp; &nbsp; if (someCondition())&nbsp; &nbsp; &nbsp; &nbsp; g = oa -> Math.max(0, (((Func<Double>) f)).apply(oa));&nbsp; &nbsp; return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());}总而言之,我认为您在问题中提出的解决方案是解决该问题的最佳解决方案。它有些冗长,但是编译器应该能够优化代码。您不会因未经检查的强制转换而失去编译时的安全性,并且不会由于创建其他Optional实例而导致运行时性能下降。

四季花海

解决方案是更改您的接口签名:double&nbsp;apply(Optional<?&nbsp;extends&nbsp;A>&nbsp;a);分析考虑一下您的界面是否仅仅是:double&nbsp;apply(A&nbsp;a);这样就永远不会发生错误。这是因为Double可分配给Object。编译器将自动调整类型。这意味着该接口实际上是:double&nbsp;apply(?&nbsp;extends&nbsp;A&nbsp;a);因此,您需要做的就是让您的界面具有这种适应能力。func(Number)可以接受Double作为参数。func(Optional<Number>)也应该接受Optional<Double>。因此,您应该? extends在您的界面上添加。
随时随地看视频慕课网APP

相关分类

Java
我要回答