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"));
}
}
A default method cannot override a method from java.
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.
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.
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.
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.
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