5

Came accross this today and spent ages trying to reproduce/figure out what was happening. Can somebody explain why this happens or is this a bug with type erasure/default methods/lambda's/polymorphism? Uncommenting the default method makes it run fine, but I would have expected this to work as is

Output:

Works fine with an object
Calling consume
Hello
Calling accept with context
Hello
Calling accept via consumer...
Exception in thread "main" java.lang.AbstractMethodError: Method test/LambdaTest$$Lambda$1.accept(Ljava/lang/Object;)V is abstract
    at test.LambdaTest$$Lambda$1/834600351.accept(Unknown Source)
    at test.LambdaTest.main(LambdaTest.java:24)

Code

package test;

import java.util.function.Consumer;

public class LambdaTest {

    public static void main(String[] args) {
        Consumer<Context> contextIgnoringObject = new ContextUnawareObject();
        contextIgnoringObject.accept(new Context());

        ContextIgnorer contextIgnoringLambda = () -> {
            System.err.println("Hello");
        };

        System.err.println("Calling consume");
        contextIgnoringLambda.consume();

        System.err.println("Calling accept with context");
        contextIgnoringLambda.accept(new Context());

        Consumer<Context> consumer = contextIgnoringLambda;

        System.err.println("Calling accept via consumer...");
        consumer.accept(new Context());

    }

    @FunctionalInterface
    public interface ContextIgnorer extends Consumer<Context> {

//      default void accept(Object object) {
//          System.err.println("Manual bridge method");
//          accept((Context)object);
//      }

        @Override
        default void accept(Context context) {
            consume();
        }

        void consume();

    }

    public static class ContextUnawareObject implements ContextIgnorer {

        @Override
        public void consume() {
            System.err.println("Works fine with an object");
        }

    }

    public static class Context {

    }

}
Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
vincent_
  • 53
  • 3
  • 3
    The provided code works fine for me. I compiled using javac 1.8.0.25, 1.8.0.40 and ecj, launched using java 1.8.0.25 and 1.8.0.40. In all of these cases I see no exception. Which compiler version are you using? Probably cleaning the project would help? – Tagir Valeev Jun 25 '15 at 11:42
  • 2
    Surely a compiler bug. That becomes especially obvious when you say that uncommenting the `default` method fixes the problem— as the compiler should *reject* that `default` method as it clashes with the erasure of other `accept` method (and the bridge method that the compiler ought to generate). Update your compiler… – Holger Jun 25 '15 at 11:56
  • Hm I've compiled and ran it with .25, .45 and .60ea b19, they all failed for me. Running on Windows x64. – vincent_ Jun 25 '15 at 11:59
  • 1
    @vincent_: did you really compile it with `javac` of these versions or are you using an IDE with its own compiler (i.e. Eclipse)? – Holger Jun 25 '15 at 12:01
  • 1
    @vincent_, rechecked again, also tried java9ea_b57. Everything works fine. Using Windows 7, x64, 64bit java. – Tagir Valeev Jun 25 '15 at 12:11
  • Yeh whoops, I accidently compiled it into the source directory with javac but ran the ones still compiled by eclipse. Works fine with javac, I'll update my eclipse, thanks. – vincent_ Jun 25 '15 at 12:17
  • 1
    Wrote an answer just for the case if anyone else will hit the same problem. – Tagir Valeev Jun 25 '15 at 12:43

1 Answers1

1

The problem appears with older ECJ compiler (3.10.0):

$ java -jar org.eclipse.jdt.core-3.10.0.v20140604-1726.jar -source 1.8 LambdaTest.java 
$ java LambdaTest
Works fine with an object
Calling consume
Hello
Calling accept with context
Hello
Calling accept via consumer...
Exception in thread "main" java.lang.AbstractMethodError: Method LambdaTest$$Lambda$1.accept(Ljava/lang/Object;)V is abstract
    at LambdaTest$$Lambda$1/424058530.accept(Unknown Source)
    at LambdaTest.main(LambdaTest.java:24)

Using org.eclipse.jdt.core_3.10.0.v20140902-0626.jar or newer solves the problem. The Oracle javac compiler has no such problem. Thus the solution would be to update your ECJ compiler or move to the javac.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334