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 :)
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.
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 :)
As of Eclipse Luna this bug has been fixed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With