14

Why does the snippet below print true on second pass through? Should it not be a new instance?

import java.util.function.Supplier;

public class Foo {
    public static void main(String[] args) throws Exception {
        Supplier<Long> old = () -> System.nanoTime();

        for (int i = 0; i < 3; i++) {
            /* false true true
            Supplier<Long> foo = System::nanoTime;*/

            Supplier<Long> foo = () -> System.nanoTime();

            /* false false false
            Supplier<Long> foo = new Supplier<Long>() {
                @Override
                public Long get() {
                    return System.nanoTime();
                }
            };
            //*/

            System.out.printf("%s %s %s%n", foo == old, foo, old);

            old = foo;
        }
    }
}

false Foo$$Lambda$2/122883338@1ddc4ec2 Foo$$Lambda$1/1534030866@133314b
true Foo$$Lambda$2/122883338@1ddc4ec2 Foo$$Lambda$2/122883338@1ddc4ec2
true Foo$$Lambda$2/122883338@1ddc4ec2 Foo$$Lambda$2/122883338@1ddc4ec2
Ilya Kozhevnikov
  • 10,242
  • 4
  • 40
  • 70

1 Answers1

11

Check out this article about how lambdas are implemented.

Essentially the compiler turned your two System.nanoTime() into the following static methods on your class:

static Long lambda$1() {
    return System.nanoTime();
}

static Long lambda$2() {
    return System.nanoTime();
}

And then created a constant reference to each target-typed to Supplier<Long> using the LambdaMetaFactory. Frankly, I'm disappointed that the Java compiler didn't realize that the lambda body was identical and only create one instance. If the Java compiler were sufficiently smart, every line should have printed true!

sh0rug0ru
  • 1,596
  • 9
  • 9
  • 2
    Even if the compiler was smart enough to fold `lambda$1()` and `lambda$2()` into one method, different lambda creation sites referring to that method still would cause creation of different object instances (and even different classes). This can easily be proven by using a method reference like `System::nanoTime` multiple times as these *do* point to the same target method as no synthetic method is generated. Still, they yield to different objects *given Oracle’s current implementation*. – Holger Oct 09 '15 at 14:16
  • Note that the observed behaviour is NOT MANDATED! The compiler or runtime MIGHT only allocate a single object and reuse it for both the lambda expressions. Such an optimisation might be added without notice in a future Java version, or might kick in during application runtime when the code is JIT optimised. See [Holger's answer here](https://stackoverflow.com/a/27524543/452775). – Lii Jul 02 '22 at 11:18