【问题标题】:Java Lambda capture or re execute?Java Lambda 捕获还是重新执行?
【发布时间】:2021-06-18 16:45:53
【问题描述】:

制作功能界面

@FunctionalInterface
public RulesFact {
    Object getPropertyValue(String propertyName);
}

方法中的 lambda 实例。

public RulesFact rulesFactImpl(Object o) {
    return propertyName->PropertyAccessorFactory.forBeanPropertyAccess(o).getPropertyValue(propertyName);
}

这种方法和上面的方法有区别吗?在上面的那个中,每次执行 lambda 时都会调用 PropertyAccessorFactory.forBeanPropertyAccess 方法吗?线程安全有区别吗?

public RulesFact rulesFactImpl(Object o) {
    BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
    return propertyName->accessor.getPropertyValue(propertyName);
}

上述反射工具的Javadocs:Package org.springframework.beans

【问题讨论】:

    标签: java lambda java-8


    【解决方案1】:

    在调用RulesFact.getPropertyValue 时执行 lambda 表达式。

    这意味着第一个版本每次调用RulesFact.getPropertyValue 时都会调用PropertyAccessorFactory.forBeanPropertyAccess(o),如果访问器可以随时间变化,这很好。

    第二个版本在调用rulesFactImpl 时调用PropertyAccessorFactory.forBeanPropertyAccess(o),并缓存该值,这对性能有好处,但如果访问器可以随时间变化,则不好。

    仅供参考:第二个版本可以使用方法参考来编写:

    public RulesFact rulesFactImpl(Object o) {
        BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
        return accessor::getPropertyValue;
    }
    

    【讨论】:

    • 而且它可以写得更短,return PropertyAccessorFactory.forBeanPropertyAccess(o) ::getPropertyValue;,而不改变语义。
    • @Holger 是的,但有时我喜欢使用中间变量来清晰和调试。一旦 JIT 处理完代码,这两种方式都不会产生影响。 ?
    • 我知道,但有时人们对用单线表达事物表现出惊人的兴趣……
    【解决方案2】:

    假设这些 API 编写得相当好,稍微有点,但可能不是你关心的方式。

    public RulesFact rulesFactImpl(Object o) {
      return propertyName->PropertyAccessorFactory.forBeanPropertyAccess(o).getPropertyValue(propertyName);
    }
    

    这会返回一个RulesFact,当调用它时,会调用forBeanPropertyAccess,然后对其执行任何操作。如果你多次调用它,它每次都会调用forBeanPropertyAccess

    public RulesFact rulesFactImpl(Object o) {
      BeanWrapper accessor = PropertyAccessorFactory.forBeanPropertyAccess(o);
      return propertyName->accessor.getPropertyValue(propertyName);
    }
    

    这会调用一次forBeanPropertyAccess 并围绕其值进行闭包。你可以把这个闭包想象成一个实例变量。在某种意义上,我们创建了RulesFact 的子类,它有一个名为accessor 的实例变量,其值仅在构造时计算一次。如果我们多次调用这个 lambda,forBeanPropertyAccess 将永远不会被再次调用;它只会多次使用原始值。

    【讨论】:

    • 两个版本都“做一个闭包”,尽管 Java 没有这么称呼它,因为它不是一个闭包。第一个版本使用o 关闭,第二个版本使用accessor 关闭。这个答案听起来像是第一个版本没有关闭,也就是创建RulesFactvirtual 子类。确实如此。
    【解决方案3】:

    它或多或少相同,但在功能上并不相同。假设PropertyAccessorFactory.forBeanPropertyAccess(o) 抛出异常。

    在第一种情况下,rulesFactImpl(...) 永远不会抛出。如果从未调用过RulesFact.getPropertyValue,则不会有例外。

    在第二种情况下,rulesFactImpl(...) 总是会抛出。

    【讨论】:

    • 另一个功能区别是第一个版本支持从forBeanPropertyAccess(o)方法更改值,而第二个版本在调用rulesFactImpl(...)时对值进行快照,因此忽略任何随时间变化访问器值。
    • @Andreas 可变对象?
    • 不是对象是否可变,而是函数是否确定性
    • @Andreas 这也不是关于决定论的。该函数可能是确定性的、纯粹的和幂等的,但如果o 在该函数then it may give different result 的上下文之外发生更改。无论如何,这只是一个轻率的言论。到目前为止,这个函数返回不同结果的最有可能的方式似乎是o 的状态发生了变化。还有其他方法,这是最有可能的
    • @Andreas 找到您可能感兴趣的this。请参阅第 3.2 节“这些问题没有一个明显正确的答案。因此,确定性是一个参数化属性:给定参数等价的含义的定义,如果..." 粗体部分是我们不同意的地方,所以这不是技术准确性的问题,而是一个问题您选择应用什么等价定义
    猜你喜欢
    • 2014-08-19
    • 1970-01-01
    • 2017-03-15
    • 2016-03-10
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    相关资源
    最近更新 更多