The used method reference has return type Integer
. But an incompatible String
is allowed in the following example.
How to fix the method <
Its the type inference that is playing its role here. Consider the generic R
in the method signature:
<R> Builder<T> with(Function<T, R> getter, R returnValue)
In the case as listed:
Builder.of(MyInterface.class).with(MyInterface::getLength, "I am NOT an Integer");
the type of R
is successfully inferred as
Serializable, Comparable<? extends Serializable & Comparable<?>>
and a String
does imply by this type, hence the compilation succeeds.
To explicitly specify the type of R
and find out the incompatibility, one can simply change the line of code as :
Builder.of(MyInterface.class).<Integer>with(MyInterface::getLength, "not valid");
It is because your generic type parameter R
can be inferred to be Object, i.e. the following compiles:
Builder.of(MyInterface.class).with((Function<MyInterface, Object>) MyInterface::getLength, "I am NOT an Integer");
In the first example, MyInterface::getLength
and "I am NOT an Integer"
helped to resolve the generic parameters T
and R
to MyInterface
and Serializable & Comparable<? extends Serializable & Comparable<?>>
respectively.
// it compiles since String is a Serializable
Function<MyInterface, Serializable> function = MyInterface::getLength;
Builder.of(MyInterface.class).with(function, "I am NOT an Integer");
MyInterface::getLength
is not always a Function<MyInterface, Integer>
unless you explicitly say so, which would lead to a compile-time error as the second example showed.
// it doesn't compile since String isn't an Integer
Function<MyInterface, Integer> function = MyInterface::getLength;
Builder.of(MyInterface.class).with(function, "I am NOT an Integer");
This answer is based on the other answers which explain why it does not work as expected.
The following code solves the problem by splitting the bifunction "with" into two fluent functions "with" and "returning":
class Builder<T> {
...
class BuilderMethod<R> {
final Function<T, R> getter;
BuilderMethod(Function<T, R> getter) {
this.getter = getter;
}
Builder<T> returning(R returnValue) {
return Builder.this.with(getter, returnValue);
}
}
<R> BuilderMethod<R> with(Function<T, R> getter) {
return new BuilderMethod<>(getter);
}
...
}
MyInterface z = Builder.of(MyInterface.class).with(MyInterface::getLength).returning(1L).with(MyInterface::getNullLength).returning(null).build();
System.out.println("length:" + z.getLength());
// YIPPIE COMPILATION ERRROR:
// The method returning(Long) in the type BuilderExample.Builder<BuilderExample.MyInterface>.BuilderMethod<Long> is not applicable for the arguments (String)
MyInterface zz = Builder.of(MyInterface.class).with(MyInterface::getLength).returning("NOT A NUMBER").build();
System.out.println("length:" + zz.getLength());
(is somewhat unfamiliar)