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<?>>
Predicate<String> lLengthIs20 = isEquals(String::length, "what the heck?!?!?")is strange behaviour. It's just the consequence of type inference.