public static class MyClass {
public String methodS(UnaryOperator<String> fn) {
return fn.apply("");
}
public Integer methodI(UnaryOperator<Integer> fn) {
return fn.apply(0);
}
public <T> T identity(T t) {
return t;
}
public <T> void test(UnaryOperator<T> fn) {
String s;
s = methodS(UnaryOperator.identity()); // OK
s = methodS(this::identity); // OK
s = methodS(String::trim); // OK
s = methodS(fn::apply); // Fail
Integer i;
i = methodI(UnaryOperator.identity()); // OK
i = methodI(this::identity); // OK
i = methodI(Math::abs); // OK
i = methodI(fn::apply); // Fail
}
public void goal() {
test(o -> {
doSomething(o);
return o;
});
}
}
I wonder why passing class method references OK but passing a method parameter not in the above example? How do you fix it?
The goal is to be able to call like in the goal() method, where a generic lambda can be written to take on the common base class object (in this case Object).
Edited: some misunderstood the question, probably because of Object as the base class. Here might be a better example with some custom common base class:
public static class MyClass {
public static class ExtS extends MyClass {};
public static class ExtI extends MyClass {};
public void common() {}
public ExtS methodS(UnaryOperator<ExtS> fn) {
return fn.apply(new ExtS());
}
public ExtI methodI(UnaryOperator<ExtI> fn) {
return fn.apply(new ExtI());
}
public <X extends MyClass> X identity(X x) {
x.common(); // can call MyClass methods
return x;
}
public <Y extends MyClass> void test(UnaryOperator<Y> fn) {
ExtS s;
s = methodS(UnaryOperator.identity()); // OK
s = methodS(this::identity); // OK
s = methodS(t -> (ExtS) fn.apply((Y) t)); // OK
s = methodS(fn::apply); // fail
ExtI i;
i = methodI(UnaryOperator.identity()); // OK
i = methodI(this::identity); // OK
i = methodI(t -> (ExtI) fn.apply((Y) t)); // OK
i = methodI(fn::apply); // fail
}
public void goal() {
test(y -> {
y.common(); // can call MyClass methods
return y;
});
test(this::identity); // doing the same as above
}
}
test(String::trim). What wouldmethodI(fn::apply);do in that case? The type would be incorrect. You cannot make a similar argument forthis::identity.Tmust be a common supertype ofIntegerandString.<Y>” inY apply(Y y)? This method does not declare a type parameter—it uses an existing type parameter (from theUnaryOperatortype). This method is not generic. In contrast,<X extends MyClass> X identity(X x)does declare a type parameter; this is a generic method. Fundamentally different.<Y extends MyClass> void test(UnaryOperator<Y> fn)allows to choose a type argument forY, however, it’s the caller oftestwho chooses whatYis. You can’t choose the method signature offn::applyinside thetestmethod because the caller already did. You still can create arbitrarily typedUnaryOperators inside the method; the type is as generic as the methodidentityis. The crucial point is who chooses the actual type arguments for a particular generic construct. For thefnparameter, it’s the caller of the method.