Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 default implementation is not available when instance is passed as its superinterface

Tags:

java

java-8

I receive a AbstractMethodError when invoking a method that, I think, should have a default implementation in the target instance.

I create a functional interface in three parameters but also derive from java.util.function.Function and provide a default implementation of Function#apply(..). I then create an instance of my interface using a 3-parameter lambda expression. Both the 3-argument method and Function#apply(..) work just fine in the created instance.

When I pass the created instance to a method expecting my interface, I can invoke Function#apply(..) from within the method and it works fine.

But when I pass the instance to a method that is expecting a Function, I receive the AbstractMethodError when I try to invoke Function#apply(..).

It seems I am missing an understanding of how and when default methods are bound to an instance. What am I doing incorrectly?

To demonstrate:

package spike;

import java.util.function.BiFunction;
import java.util.function.Function;

public class ReductionProblem {

    interface F3<T, U, V, R> extends Function<T, BiFunction<U, V, R>> {

        default BiFunction<U, V, R> apply(final T t) {
            return (U u, V v) -> apply(t, u, v);
        }

        R apply(T t, U u, V v);

    }

    private static <T, U, V, R> BiFunction<U, V, R> workingReduce(
            F3<T, U, V, R> f, T t) {
        return f.apply(t);
    }

    private static <T, U, V, R> BiFunction<U, V, R> brokenReduce(
            Function<T, BiFunction<U, V, R>> f, T t) {
        return f.apply(t);
    }

    public static void main(String[] args) {

        /*
         * Object is instantiated here, right? So, Function#apply(Integer)
         * should be defined and ready to go.
         */
        final F3<Integer, Integer, String, Integer> f3 = (a, b, c) -> a * b
                * c.length();

        final Integer a = 3, b = 13;
        final String c = "see";

        final Integer expected = a * b * c.length();

        Integer v1 = f3.apply(a, b, c);
        display("invoke with 3", expected, v1);

        /*
         * It appears that f3 can indeed behave as a Function<Integer,
         * BiFunction<>> ...
         */
        Integer v2 = f3.apply(a).apply(b, c);
        display("invoke with 1 then 2", expected, v2);

        /*
         * From inside a method, no problem...
         */
        Integer v3 = workingReduce(f3, a).apply(b, c);
        display("invoke with 1 inside special reducer", expected, v3);

        /*
         * But when passed explicitly as a Function, AbstractMethodError is
         * thrown from inside the reduction method. So, it seems that the
         * instantiation of the object with the default method implementation
         * does not occur where I am supposing it does.
         */
        Integer v4 = brokenReduce(f3, a).apply(b, c);
        display("invoke with 1 inside general reducer", expected, v4);

    }

    private static void display(String label, Object expected, Object actual) {

        System.out.println(label + ":"
                + (actual.equals(expected) ? "pass" : "fail"));

    }

}
like image 258
aztecrex Avatar asked Jun 07 '14 04:06

aztecrex


People also ask

Can we override default method in Java interface 8?

A default method cannot override a method from java.

Can we implement default method of interface?

Interfaces can have default methods with implementation in Java 8 on later. Interfaces can have static methods as well, similar to static methods in classes. Default methods were introduced to provide backward compatibility for old interfaces so that they can have new methods without affecting existing code.

What is default method Java 8?

Default methods in Java 8 also known as defender methods allow Java developers to add new methods to the existing interfaces in their code without breaking their current implementation.

What is the use of default and static methods in interface Java 8?

Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces. A static method is a method that is associated with the class in which it is defined rather than with any object.


1 Answers

This seems like a bug in whatever compiler you are using. I was only able to reproduce it with the Eclipse compiler. It works fine with

[~/workspace/Example/bin]$ javac -version
javac 1.8.0_05
[~/workspace/Example/bin]$ 

The error here seems to be that the compiler doesn't add an invokeinterface for invoking the method but instead references the abstract method itself. Or maybe it builds the instance resulting from the lambda without the default method implementation, but that doesn't seem likely since other invocations work.

like image 154
Sotirios Delimanolis Avatar answered Sep 18 '22 13:09

Sotirios Delimanolis