Java 8 中的有效决赛与决赛

Posted

技术标签:

【中文标题】Java 8 中的有效决赛与决赛【英文标题】:Effective final vs final in Java 8 【发布时间】:2022-01-20 13:32:38 【问题描述】:

我在下面的 main 方法中执行:

int secrete = 42;
for (int i = 0; i < 5; i++) 
    Consumer<String> myprinter2 =
                    msg -> 
                        System.out.println("consuming " + msg + " ," + secrete);
                    ;
     myprinter2.accept(myprinter2.toString());

上述代码的输出是:

consuming Main$$Lambda$1/1324119927@6d311334 ,42
consuming Main$$Lambda$1/1324119927@682a0b20 ,42
consuming Main$$Lambda$1/1324119927@3d075dc0 ,42
consuming Main$$Lambda$1/1324119927@214c265e ,42
consuming Main$$Lambda$1/1324119927@448139f0 ,42

如果我将secrete 更改为最终结果,则输出为:

consuming Main$$Lambda$1/2003749087@41629346 ,42
consuming Main$$Lambda$1/2003749087@41629346 ,42
consuming Main$$Lambda$1/2003749087@41629346 ,42
consuming Main$$Lambda$1/2003749087@41629346 ,42
consuming Main$$Lambda$1/2003749087@41629346 ,42

secrete 即使我没有将它声明为 final,它实际上也是 final,那么为什么当我没有将它声明为 final 时,每个 lambda 都被视为一个新对象?

【问题讨论】:

有趣的观察。我想区别在于显式最终变量可以被视为编译时常量,但有效的最终变量不能。 另外有趣的是,如果你将final int secrete 移动到for 循环中,你也会得到第二个输出。 @khelwood 你的理论对我来说没有意义。编译器是决定什么是有效最终的,不是吗?因此,如果编译器有效地识别最终结果,那么我认为编译器应该能够明确地处理最终结果和有效最终结果。 (并不是说我知道这段代码中发生了什么。) @BasilBourque 好吧,实际上最终意味着变量在初始化后不会改变,而不是它一定是编译时常量。在这种情况下,它可以成为一个,但也许编译器不会寻找这组特定的情况。但也许有人有更多的技术洞察力。 @BasilBourque 在处理 final 和有效 final 方面没有技术障碍,但正如 this answer 中所解释的那样,该规范没有考虑有效的 final 变量编译时常量。因此,只要形式语义很重要,编译器就不能将它们视为编译时常量。在这个具体的例子中,没关系,as explained here 这是一个实现细节。但是,优化这种情况将是一种特殊情况,会使编译器更加复杂。 【参考方案1】:

“有效最终”在技术上不是必需的,没有它也可以完成。但是语言设计者设置这个限制是为了避免混淆,因为如果变量不断变化,lambda 会看到什么值,初始值还是最新值?其他有 lambda 的语言没有这个限制,规范设定了这个用例的期望。

给定以下代码:

import java.util.function.Consumer;


class Main   
  public static void main(String args[])  
    int i = 42;
    final int j = 41;
    for (int k = 0; k < 5; k++) 
      Consumer<String> x = msg -> System.out.printf("x=%s, i=%d%n", msg, i);
      Consumer<String> y = msg -> System.out.printf("y=%s, j=%d%n", msg, j);
      Consumer<String> z = msg -> System.out.printf("z=%s%n", msg);
      x.accept(x.toString());
      y.accept(y.toString());
      z.accept(z.toString());
    
   

当我们使用javap -c -v Main.class 检查生成的字节码时,我们看到:

11: invokedynamic #7,  0              // InvokeDynamic #0:accept:(I)Ljava/util/function/Consumer;
16: astore_3
17: invokedynamic #11,  0             // InvokeDynamic #1:accept:()Ljava/util/function/Consumer;
22: astore        4
24: invokedynamic #14,  0             // InvokeDynamic #2:accept:()Ljava/util/function/Consumer;

我们可以看到 lambda 是如何翻译的。对应的static 方法表明第一个 lambda 是一个捕获 lambda,并且在翻译后有一个整数第一个参数 (#71),而其他的则没有。

BootstrapMethods:
  0: #63 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #70 (Ljava/lang/Object;)V
      #71 REF_invokeStatic Main.lambda$main$0:(ILjava/lang/String;)V
      #74 (Ljava/lang/String;)V
  1: #63 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #70 (Ljava/lang/Object;)V
      #75 REF_invokeStatic Main.lambda$main$1:(Ljava/lang/String;)V
      #74 (Ljava/lang/String;)V
  2: #63 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #70 (Ljava/lang/Object;)V
      #78 REF_invokeStatic Main.lambda$main$2:(Ljava/lang/String;)V
      #74 (Ljava/lang/String;)V

所以,这就是 lambda 的翻译方式。您可以在this article找到更多详细信息。

【讨论】:

以上是关于Java 8 中的有效决赛与决赛的主要内容,如果未能解决你的问题,请参考以下文章

第十六届智能车竞赛全国总决赛究竟该怎么举办讨论中的“混沌”现象

第十六届全国大学生智能车竞赛线上总决赛比赛时间与直播链接

第十七届智能汽车竞赛总决赛线上赛流程

赛事通知 | 湖南省第二届人工智能产业创新与应用大赛决赛即将开始!

第二届华中科技大学研究生人工智能创新大赛决赛顺利举行

第十六届全国大学生智能车竞赛全国总决赛获奖排行榜