9

source code(of course the project have many other classes)

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;


public class Test {
    public static void main(String[] args) {
        Map<Integer, Integer> src = new HashMap<>();
        Map<Integer, List<Integer>> dst = new HashMap<>();
        Optional<Object> f = dst.values().stream().flatMap((Function<List<Integer>, Stream<?>>) integers -> integers.stream()).filter(b -> !src.containsKey(b)).findFirst();
        f.ifPresent(b -> {
            throw new IllegalStateException("exception [" + b + "]");
        });
    }
}

1.copy project to different path

d:\Users\shell\Documents\workplace\bak\1

d:\Users\shell\Documents\workplace\bak\2

2.execute mvn package in both path

3.check the class file

execute command javap -p in d:\Users\shell\Documents\workplace\bak\1\data\target\classes

public class Test {
    public Test();
    public static void main(java.lang.String[]);
    private static void lambda$main$84(java.lang.Object);
    private static boolean lambda$main$83(java.util.Map, java.lang.Object);
    private static java.util.stream.Stream lambda$main$82(java.util.List);
}

execute command javap -p in d:\Users\shell\Documents\workplace\bak\2\data\target\classes

public class Test {
    public Test();
    public static void main(java.lang.String[]);
    private static void lambda$main$75(java.lang.Object);
    private static boolean lambda$main$74(java.util.Map, java.lang.Object);
    private static java.util.stream.Stream lambda$main$73(java.util.List);
}

why the number of lambda function is different?

is there any way i can make them same?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
shell
  • 93
  • 5
  • 4
    Why does the name of the lamba method matter to you? Note that it is private, too, so should not affect any other classes. – Thilo Mar 10 '16 at 07:54
  • fork=true does not work for me. – shell Mar 10 '16 at 08:05
  • @Thilo can't speak for the OP, but having a deterministic, consistent output can help make large complex builds faster by skipping some parts – JB Nizet Mar 10 '16 at 08:28
  • 1
    @JB Nizet: How can the name of a `private` method, deterministic or not, influence the speed of a complex build? That sounds like a big fail on modularization of the software project. – Holger Mar 10 '16 at 11:12
  • @Holger when subsequent tasks need to copy files, or jar them then copy, possibly to remote locations, having class files and jar files that end up being exactly identical allows the build process to skip certain tasks. gradle for example can detect that the inputs of a tasks are identical to the previous ones, and skip the task, for faster incremental builds – JB Nizet Mar 10 '16 at 16:01
  • @JB Nizet: You have the same issue without lambdas when the order of members or attributes or items in the constant pool of a class file changes and it’s completely unspecified and may change even with the same compiler if it uses a hash map which randomizes the hashing as some jdk7 versions do. But you have to build and deploy a module/jar as soon as a single class changes and wouldn’t rebuild a module when there’s no change at all. When it comes to *dependent* modules, they shouldn’t get recompiled based on the exact byte contents, but only the *exported API* of the classes. – Holger Mar 10 '16 at 18:58
  • @Holger agreed, but the fact that something else might cause the bytecode to change without any change in the source code doesn't make it less desirable (although not required) to have a deterministic, consistent output from the compiler. – JB Nizet Mar 10 '16 at 20:07

2 Answers2

13

Looking into javac source you may notice that the corresponding counter (which is appended to the lambda method name) is defined as an instance field in the LambdaAnalyzerPreprocessor which is reused for the whole compilation process. It's incremented on every lambda occurrence. So if I compile your class only, I will have numbers started from 0:

> javac Test.java
> javap -p Test
Compiled from "Test.java"
public class Test {
  public Test();
  public static void main(java.lang.String[]);
  private static void lambda$main$2(java.lang.Object);
  private static boolean lambda$main$1(java.util.Map, java.lang.Object);
  private static java.util.stream.Stream lambda$main$0(java.util.List);
}

But if I create one more class

public class Test2 { 
    Runnable r = () -> {};
}

And compile them together I will see the counter incremented:

> javac Test2.java Test.java 
> javap -p Test
Compiled from "Test.java"
public class Test {
  public Test();
  public static void main(java.lang.String[]);
  private static void lambda$main$3(java.lang.Object);
  private static boolean lambda$main$2(java.util.Map, java.lang.Object);
  private static java.util.stream.Stream lambda$main$1(java.util.List);
}

So this is not the maven problem, this is how javac compiler works.

If you definitely need stable compilation results I may suggest you to try Eclipse Compiler for Java. Seems that it has no such problem:

>java -jar org.eclipse.jdt.core_3.11.1.v20150902-1521.jar -8 Test2.java Test.java
>javap -p Test
Compiled from "Test.java"
public class Test {
  public Test();
  public static void main(java.lang.String[]);
  private static java.util.stream.Stream lambda$0(java.util.List);
  private static boolean lambda$1(java.util.Map, java.lang.Object);
  private static void lambda$2(java.lang.Object);
}

Refer to this question on how to integrate ecj with maven.

Community
  • 1
  • 1
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
0

FWIW, this used to be caused by a bug in OpenJDK. It was fixed in December 2016: https://bugs.openjdk.java.net/browse/JDK-8067422

Currently, when lambda names (for non-serializable lambdas) are generated, the exact name depends on the number of (non-serializable) lamdbas generated so far by the current javac instance. The reason is that the counter for lambda names is not per-file, but per-javac instance. For example, consider:

public class L1 {
   private Runnable r = () -> { };
}

public class L2 {
    private Runnable r = () -> { };
}

doing: javac L1.java L2.java will make L1's lambda use lambda$new$0 and L2's lambda$new$1, while: javac L2.java L1.java will lead to the opposite name assignment.

The problem seems to be simple: LambdaToMethod.LambdaAnalyzerPreprocessor.lambdaCount is not reset before starting with a new top-level.

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46