为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

DIEA 2021-05-14 22:13:11
我有一些具有这种一般结构的代码: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());    }编译器接受了这一点,但是除了使类型检查器满意之外,间接调用实际上并不是我所说的“优雅”。
查看完整描述

3 回答

?
慕田峪7331174

TA贡献1828条经验 获得超13个赞

试试这个奇怪的把戏:

g = oa -> Math.max(0, f.apply(oa.map(a -> a)));
                     // ^----------^

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

Ideone demo

这确实存在创建新Optional实例的缺点。


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


就个人而言,我认为您的“迄今为止最好的”并不特别糟糕。当然,这取决于实际代码的外观。


查看完整回答
反对 回复 2021-05-26
?
翻阅古今

TA贡献1780条经验 获得超5个赞

总的来说,我发现Java中超界的类型推断是一个噩梦。.有很多编译器错误,通常很难推理为什么编译器会拒绝某种语法而不拒绝其他语法。


就是说,您可以通过强制转换f为Func<Double>类型来解决这种问题。这并不是完全安全的方法。如果f具有任何状态,我们可以假设其下限f是Double在现实中可能是Number或Object。使用强制转换,您不会招致@AndyTurner提供的解决方案的性能损失,但是未经检查的强制转换也不是您的朋友。


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

    // Sometimes amend the function to do something slightly different

    Func<? super Double> g = f;

    if (someCondition())

        g = oa -> Math.max(0, (((Func<Double>) f)).apply(oa));


    return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());

}

总而言之,我认为您在问题中提出的解决方案是解决该问题的最佳解决方案。它有些冗长,但是编译器应该能够优化代码。您不会因未经检查的强制转换而失去编译时的安全性,并且不会由于创建其他Optional实例而导致运行时性能下降。


查看完整回答
反对 回复 2021-05-26
?
四季花海

TA贡献1811条经验 获得超5个赞

解决方案是更改您的接口签名:

double apply(Optional<? extends A> a);

分析

考虑一下您的界面是否仅仅是:

double apply(A a);

这样就永远不会发生错误。

这是因为Double可分配给Object。编译器将自动调整类型。这意味着该接口实际上是:

double apply(? extends A a);

因此,您需要做的就是让您的界面具有这种适应能力。

func(Number)可以接受Double作为参数。

func(Optional<Number>)也应该接受Optional<Double>

因此,您应该? extends在您的界面上添加。


查看完整回答
反对 回复 2021-05-26
  • 3 回答
  • 0 关注
  • 111 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信