Is there a way to tell Java to NOT try to infer a type from a method reference that uses primitive types?
Here is a method that I wrote, the reason for this is irrelevant right now:
public static <F, T> Predicate<F> isEquals(
Function<F, T> aInMapFunction, T aInExpectedValue)
{
return aInActual -> Objects.equals(
aInMapFunction.apply(aInActual), aInExpectedValue);
}
Now, what if you pass a method reference to "isEquals" that returns a primitive type?
Predicate<String> lLengthIs20 = isEquals(String::length, 20);
This is all fine and dandy, except that Java will also accept this strange usage:
Predicate<String> lLengthIs20 = isEquals(String::length, "what the heck?!?!?");
This is because the compiler will infer type parameter T as "Serializable & Comparable<? extends Serializable & Comparable<?>>", which will accept both Integer and String types.
This is undesirable, in my case, as I would like a compilation error rather than Java figuring out some crazy type argument. For my thing, I can also explicitly override method "isEquals" to take specific primitive types. For example:
public static <F> Predicate<F> isEquals(
ToIntFunction<F> aInMapFunction, int aInExpectedValue)
{
return aInActual ->
aInMapFunction.applyAsInt(aInActual) == aInExpectedValue;
}
This works fine, this method is invoked rather than the Object one when I pass in a method that returns a primitive int. The problem is that I still need the Object method, I cannot remove it, which will still cause the compiler to accept the weird invocation I listed above.
So the question is: is there a way for me to tell Java to not use the Object version of isEquals when the method reference returns a primitive type? I couldn't find anything, I'm feeling I'm out of luck in this one.
(NOTE: the actual implementation of the object version of isEquals works fine and should be safe. This is because Object.equals and Objects.equals accept Object parameters and a String object will never be equals to an Integer object. Semantically, however, this looks weird)
EDIT: after the comment from "paranoidAndroid", one idea that I just had is to wrap the method reference the following way:
public static <T> Function<T, Integer> wrap(ToIntFunction<T> aInFunction)
{
return aInFunction::applyAsInt;
}
And now,
Predicate<String> lLengthIs20 = isEquals(wrap(String::length), "what the heck?!?!?");
... generates a compilation error. Still not great though, maybe there is a better way. At least this is better than passing the type in explicitly, which kind of beats the purpose.
EDIT 2: I'm in Java 8 right now. Java 11 might behave differently here, I didn't test.
EDIT 3: I'm thinking there is nothing we can do here, this is just an implication of how type inference works in Java. Here is another example:
public static <T> boolean isEquals(T t1, T t2) {
return Objects.equals(t1, t2);
}
with this method, the following expression is perfectly valid:
System.out.println(isEquals(10, "20"));
This works because Java will try to resolve the type for T based on a common upper bound. It just happens that both Integer and String share the same upper bound Serializable & Comparable<? extends Serializable & Comparable<?>>