Home > database >  Effective final vs final in Java 8
Effective final vs final in Java 8

Time:12-18

I am executing below in main method:

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

The output for the above code is:

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

If I change secrete to be final, then the output is:

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 is effectively final even if I do not declare it final, so why is each lambda considered a new object when I do not declare it final?

CodePudding user response:

This prints false:

int x = 42;
String a = "hi"   x;
String b = "hi"   x;

System.out.println(a == b);

This prints true:

final int x = 42;
String a = "hi"   x;
String b = "hi"   x;

System.out.println(a == b);

So, final influences if the concatenation " ," secrete in System.out.println("consuming " msg " ," secrete); is done at compile time. Without final, " ," secrete results in a different String object in every iteration and therefore, JIT has to create a new Consumer in each iteration. With final, System.out.println("consuming " msg " ," secrete); depends only on msg and therefore, only one Consumer is created.

CodePudding user response:

"Effectively final" is not technically required, it could've been done without. But the language designers put this restriction to avoid confusion, because if the variable kept changing, what value would the lambda see, the initial, or the latest? Other languages that have lambda don't have this restriction, and the spec sets the expectation for this use case.

Given the following code:

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());
    }
  } 
}

When we inspect the generated bytecode with javap -c -v Main.class, we see:

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;

We can see how the lambdas are translated. The corresponding static methods show that the first lambda is a capturing lambda and has an integer 1st parameter (#71) after translation, while the other ones don't.

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

So, it's just how lambdas are translated. You can find more details in this article.

  •  Tags:  
  • java
  • Related