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

方法参考缓存在Java 8中是个好主意吗?

方法参考缓存在Java 8中是个好主意吗?

凤凰求蛊 2019-08-06 17:21:37
方法参考缓存在Java 8中是个好主意吗?考虑我有以下代码:class Foo {    Y func(X x) {...}     void doSomethingWithAFunc(Function<X,Y> f){...}    void hotFunction(){         doSomethingWithAFunc(this::func);    }}假设hotFunction经常被调用。那么缓存是否可取this::func,也许是这样的:class Foo {      Function<X,Y> f = this::func;      ...      void hotFunction(){         doSomethingWithAFunc(f);      }}就我对java方法引用的理解而言,虚拟机在使用方法引用时会创建匿名类的对象。因此,缓存引用将仅创建该对象一次,而第一种方法在每个函数调用上创建它。它是否正确?是否应缓存出现在代码中热位置的方法引用,或者VM是否能够对此进行优化并使缓存变得多余?是否存在关于此的一般最佳实践,或者这种高度VM实现是否特定于此类缓存是否有用?
查看完整描述

3 回答

?
一只甜甜圈

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

遗憾的是,如果lambda作为一个你想要在将来某个时候删除的监听器传递,那么它是一个很好的理想的一种情况。将需要缓存的引用作为传递另一个:: method引用将不会被视为删除中的同一对象,并且原始文件将不会被删除。例如:

public class Example{
    public void main( String[] args )
    {
        new SingleChangeListenerFail().listenForASingleChange();
        SingleChangeListenerFail.observableValue.set( "Here be a change." );
        SingleChangeListenerFail.observableValue.set( "Here be another change that you probably don't want." );

        new SingleChangeListenerCorrect().listenForASingleChange();
        SingleChangeListenerCorrect.observableValue.set( "Here be a change." );
        SingleChangeListenerCorrect.observableValue.set( "Here be another change but you'll never know." );
    }

    static class SingleChangeListenerFail
    {
        static SimpleStringProperty observableValue = new SimpleStringProperty();

        public void listenForASingleChange()
        {
            observableValue.addListener(this::changed);
        }

        private<T> void changed( ObservableValue<? extends T> observable, T oldValue, T newValue )
        {
            System.out.println( "New Value: " + newValue );
            observableValue.removeListener(this::changed);
        }
    }

    static class SingleChangeListenerCorrect
    {
        static SimpleStringProperty observableValue = new SimpleStringProperty();
        ChangeListener<String> lambdaRef = this::changed;

        public void listenForASingleChange()
        {
            observableValue.addListener(lambdaRef);
        }

        private<T> void changed( ObservableValue<? extends T> observable, T oldValue, T newValue )
        {
            System.out.println( "New Value: " + newValue );
            observableValue.removeListener(lambdaRef);
        }
    }}

在这种情况下本来不需要lambdaRef会很好。


查看完整回答
反对 回复 2019-08-06
?
繁花不似锦

TA贡献1851条经验 获得超4个赞

据我所知,语言规范允许这种优化,即使它改变了可观察的行为。请参阅JSL8§15.13.3部分中的以下引用:

§15.13.3方法参考的运行时评估

在运行时,方法引用表达式的求值类似于类实例创建表达式的求值,只要正常完成产生对对象的引用即可。[..]

[..] 要么与下面的性质的类的新实例被分配和初始化,或现有的实例与下面的属性的类的被引用。

一个简单的测试表明,静态方法(can)的方法引用为每个评估产生相同的引用。以下程序打印三行,其中前两行相同:

public class Demo {
    public static void main(String... args) {
        foobar();
        foobar();
        System.out.println((Runnable) Demo::foobar);
    }
    public static void foobar() {
        System.out.println((Runnable) Demo::foobar);
    }}

我不能为非静态函数重现相同的效果。但是,我没有在语言规范中找到任何禁止此优化的内容。

因此,只要没有性能分析来确定这种手动优化的价值,我强烈建议不要这样做。缓存会影响代码的可读性,并且不清楚它是否具有任何价值。过早优化是万恶之源。


查看完整回答
反对 回复 2019-08-06
  • 3 回答
  • 0 关注
  • 735 浏览

添加回答

举报

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