Java 8: Duplicate method name&signature lambda
Asked Answered
M

2

10

I was playing around with Java 8 lambdas, method references and interface default methods to explore the wonderful world of currying, and then I got this Java error I cannot understand.

Here is the code :

public class Main {

    public interface CurryBiConsumer<T, U> extends BiConsumer<T, U> {
        default public CurryConsumer<U> curryFirst(T t) {
            return (u) -> accept(t, u);
        }
        default public CurryConsumer<T> currySecond(U u) {
            return (t) -> accept(t, u);
        }
    }

    public interface CurryConsumer<T> extends Consumer<T> {
        default public Runnable curry(T t) {
            return () -> accept(t);
        }
    }

    static void execute(Runnable r) {
        System.out.println("BEFORE");
        r.run();
        System.out.println("AFTER");
    }

    static void display(String str, int count) {
        System.out.println("DISP: " + str + " " + count);
    }

    public static void main(String[] args) {
        CurryBiConsumer<String, Integer> bc = Main::display;

        execute(bc.curryFirst("Salomon").curry(42));
    }
}

Eclipse gives me no error but when I run it, I get this runtime error :

Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:328)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:296)
    at com.test8.Main.main(Main.java:34)
Caused by: java.lang.ClassFormatError: Duplicate method name&signature in class file com/test8/Main$$Lambda$1
    at sun.misc.Unsafe.defineAnonymousClass(Native Method)
    at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:324)
    at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:194)
    at java.lang.invoke.LambdaMetafactory.altMetafactory(LambdaMetafactory.java:473)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:301)
    ... 2 more

Can someone explain What is this error, why it is happening and how to get around it ?

Thanks :)

Madelenemadelin answered 27/3, 2014 at 11:43 Comment(4)
CurryBiConsumer needs to be a functional interfaceChemical
What version of java 8 are you using? your code compiles and runs as expected on my machine. Have you tried to run it with java -jar instead of through eclipse?Delrosario
Just curious, why would you want to extend functional interfaces, it seems not so smart to do so in my opinion.Paulita
I find extending functional interfaces to add default method very handy. They remain functional interfaces and you can add behaviour to them. Line 34 is CurryBiConsumer<String, Integer> bc = Main::display;Madelenemadelin
V
13

Set the system property jdk.internal.lambda.dumpProxyClasses to point to a directory on your filesystem. You'll get the bytecode of the synthesized anonymous class com/test8/Main$$Lambda$1 dumped to that location. Open that class file with javap to see what has happened. You should find two declarations of the same method there.

Update

This is the javap output produced by the above procedure, when compiled with Eclipse:

final class test.Main$$Lambda$1 implements test.Main$CurryBiConsumer {
  public void accept(java.lang.Object, java.lang.Object);
    Code:
       0: aload_1       
       1: checkcast     #14                 // class java/lang/String
       4: aload_2       
       5: checkcast     #16                 // class java/lang/Integer
       8: invokestatic  #22                 // Method test/Main.lambda$0:(Ljava/lang/String;Ljava/lang/Integer;)V
      11: return        

  public void accept(java.lang.Object, java.lang.Object);
    Code:
       0: aload_1       
       1: checkcast     #14                 // class java/lang/String
       4: aload_2       
       5: checkcast     #16                 // class java/lang/Integer
       8: invokestatic  #22                 // Method test/Main.lambda$0:(Ljava/lang/String;Ljava/lang/Integer;)V
      11: return        
}

And this is how it should be, and what javac does:

final class test.Main$$Lambda$1 implements test.Main$CurryBiConsumer {
  public void accept(java.lang.Object, java.lang.Object);
    Code:
       0: aload_1       
       1: checkcast     #14                 // class java/lang/String
       4: aload_2       
       5: checkcast     #16                 // class java/lang/Integer
       8: invokevirtual #20                 // Method java/lang/Integer.intValue:()I
      11: invokestatic  #26                 // Method test/Main.display:(Ljava/lang/String;I)V
      14: return        
}

Conclusion: this is an issue with the Eclipse JDT compiler. Someone should report it :)

Update

As of Eclipse Luna this bug has been fixed.

Verine answered 27/3, 2014 at 12:0 Comment(4)
Can you reproduce the issue?Delrosario
In Eclipse, yes; with javac, no. It's an Eclipse bug.Verine
Apropos Java 8 bugs, have you followed this question yesterday, where it turns out that javac compiles (i) -> i++ into (i) -> i+1? #22648579Verine
Too late to report it, I have Luna and it doesn't reproduce there.Verine
U
0

In my case, changing the JRE Runtime installed in eclipse solved the issue

Umbra answered 22/11, 2023 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.