Could anyone help me understand why in 1 situation I do not have ClassCastException? At least String::trim is not a MagicFunction.
public class Main { @FunctionalInterface interface MagicFunction extends Function<String, String> { } public static void main(String[] args) throws IOException { // 1. OK final MagicFunction fun1 = String::trim; // 2. java.lang.ClassCastException Function<String, String> trim = String::trim; final MagicFunction fun2 = (MagicFunction) trim; } }
Advertisement
Answer
So, method references (like String::trim
) are a bit weird; unlike most expressions in Java, they don’t really have a type of their own. Something like this:
System.out.println((String::trim).getClass());
won’t even compile, because it doesn’t give the compiler enough information about what type String::trim
should be.
Instead, the type of every method reference must be inferred from the surrounding context, e.g. by being the right-hand-side of an assignment statement (using the type of the variable on the left-hand-side) or by being passed directly to a method (using the type of the method parameter). The compiler then generates a class for you that implements the appropriate type using the method in question. Something like this:
final MagicFunction fun1 = String::trim;
is effectively equivalent to this:
final MagicFunction fun1 = new MagicFunction() { public String apply(final String s) { return s.trim(); } };
Note that this only works for specific types, called “functional interface” types. The detailed rules are a bit complicated, but the basic idea is that it has to be an interface type with exactly one abstract method. The method reference supplies the implementation of that method. (And of course, the signature of this method has to be compatible with the signature of the method reference; you can’t use String::trim
to implement Function<Integer, Integer>
.)