Lambda表达式和等效的匿名类

huangapple go评论65阅读模式
英文:

Lambda expression and equivalent anonymous class

问题

以下是翻译好的部分:

如果我有一个接受单方法接口作为参数的方法,我可以这样调用它:

foo(new Bar() {
    @Override
    public String baz(String qux) {
        return modify(qux) + transmogrify(qux);
    }
});

但是如果我需要在一个紧密的循环中调用foo数百万次,我可能更喜欢避免在每次循环中都创建一个新的匿名类的实例:

final Bar bar = new Bar() {
    @Override
    public String baz(String qux) {
        return modify(qux) + transmogrify(qux);
    }
};

while (...) {
    foo(bar);
}

现在,如果我用lambda表达式替换匿名类:

while (...) {
    foo(qux -> modify(qux) + transmogrify(qux));
}

这个lambda表达式和上面匿名类示例中的第一个还是第二个片段等效吗?

英文:

If I have a method that takes a single-method interface as an argument, I can call it like this:

foo(new Bar() {
    @Override
    public String baz(String qux) {
        return modify(qux) + transmogrify(qux);
    }
}

But if I have to call foo millions of times in a tight loop, I might prefer to avoid creating a new instance of the anonymous class every time through the loop:

final Bar bar = new Bar() {
        @Override
        public String baz(String qux) {
            return modify(qux) + transmogrify(qux);
        }
    };

while (...) {
    foo(bar);
}

Now if I replace the anonymous class with a lambda expression:

while (...) {
    foo(qux -> modify(qux) + transmogrify(qux));
}

Is this lambda expression equivalent to the first or second snippet from the above examples of anonymous class?

答案1

得分: 2

我认为更像是第二种情况,因为Bar只会有一个由lambda创建的实例。

我编写了一个Test类:

public class LambdaTest {
    //为每次循环创建一个新的runnable
    void test1(){
        while (true){
            invoke(new Runnable() {
                @Override
                public void run() {
                    System.out.println("---");
                }
            });
        }
    }

    //仅创建一个Runnable
    void test2(){
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println("---");
            }
        };
        while (true){
            invoke(runnable);
        }
    }

    //使用lambda
    void test3(){
        while (true){
            invoke(() -> {
                System.out.println("---");
            });
        }
    }
    private void invoke(Runnable runnable){
    }
}

以下是每种方法的字节码:

test1:
         0: aload_0
         1: new           #2                  // class LambdaTest$1
         4: dup
         5: aload_0
         6: invokespecial #3                  // Method LambdaTest$1."<init>":(LLambdaTest;)V
         9: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
        12: goto          0

test2:
         0: new           #5                  // class LambdaTest$2
         3: dup
         4: aload_0
         5: invokespecial #6                  // Method LambdaTest$2."<init>":(LLambdaTest;)V
         8: astore_1
         9: aload_0
        10: aload_1
        11: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
        14: goto          9

test3:
         0: aload_0
         1: invokedynamic #7,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         6: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
         9: goto          0

在test1中,每次循环都会创建一个新的匿名类实例并调用init方法。
在test2中,只会有一个匿名类实例,循环中的操作码数量最少。
在test3中,与test2之间唯一的区别是循环中的invokevirtual操作码之前添加了invokedynamic

根据这篇文章invokedynamic会调用一个引导方法来在第一次调用时创建匿名类的实例,之后会在其生命周期内重复使用已创建的实例。

所以我的建议是:随时使用lambda,无需担心开销。

英文:

I think it's more like the second one because there will only be one instance of Bar created by the lambda.

I write a Test class:

public class LambdaTest {
    //create a new runnable for each loop
    void test1(){
        while (true){
            invoke(new Runnable() {
                @Override
                public void run() {
                    System.out.println(&quot;---&quot;);
                }
            });
        }
    }

    //create only one Runnable
    void test2(){
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                System.out.println(&quot;---&quot;);
            }
        };
        while (true){
            invoke(runnable);
        }
    }

    //use lambda
    void test3(){
        while (true){
            invoke(()-&gt;{
                System.out.println(&quot;---&quot;);
            });
        }
    }
    private void invoke(Runnable runnable){
    }
}

And here is the bytecode for each method:

test1:
         0: aload_0
         1: new           #2                  // class LambdaTest$1
         4: dup
         5: aload_0
         6: invokespecial #3                  // Method LambdaTest$1.&quot;&lt;init&gt;&quot;:(LLambdaTest;)V
         9: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
        12: goto          0

test2:
         0: new           #5                  // class LambdaTest$2
         3: dup
         4: aload_0
         5: invokespecial #6                  // Method LambdaTest$2.&quot;&lt;init&gt;&quot;:(LLambdaTest;)V
         8: astore_1
         9: aload_0
        10: aload_1
        11: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
        14: goto          9

test3:
         0: aload_0
         1: invokedynamic #7,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         6: invokevirtual #4                  // Method invoke:(Ljava/lang/Runnable;)V
         9: goto          0

In test1, each loop will crate a new instance of the anonymous class and call init method.
In test2, there will only be one instance of the anonymous class, and has the smallest number of opcode in the loop.
In test3, the only difference between test2 is that there add a invokedynamic before invokevirtual opcode in the loop.

According the this article, invokedynamic will call a bootstrap method to create an instance of the anonymous class for the first time, after that it will use the created instance for the rest of its life.

So my suggestion is: Use lambda when ever you like, don't have to care about the overhead.

huangapple
  • 本文由 发表于 2020年9月30日 00:22:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/64123700.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定