Would like to get some advise on it
My class structure is like this
public abstract class Base {}
public class Derived1 extends Base {}
public class Derived2 extends Base {}
public class ServiceClass {
public String method1(Derived1 derived1) {
return "derived1";
}
public String method2(Derived2 derived2) {
return "derived2";
}
}
In my main code (MainProg),I am trying to use the same function to refer to the 2 methods in service class and seeing the compilation error as put in the comment
public class MainProg {
public static void main(String[] args) {
ServiceClass serviceClass = new ServiceClass();
//Incompatible types: Base is not convertible to Derived1
Function<? extends Base,String> function1 = serviceClass::method1;
//Incompatible types: Object is not convertible to Derived1
Function<?,String> function2 = serviceClass::method1;
//Incompatible types: Base is not convertible to Derived2
Function<? super Base,String> function3 = serviceClass::method2;
}
}
Is there a way to declare my function object so that same function object can be used to refer to methods taking different type of arguments?
CodePudding user response:
Yes, by using an intermediary variable:
Function<Derived1, String> function1 = serviceClass::method1;
Function<Derived2, String> function2 = serviceClass::method2;
Function<? extends Base, String> function = condition ? function1 : function2;
That said, you won't be able to invoke function
, because you don't know whether to pass a Derived1
or a Derived2
, or as the compiler puts it:
function.apply(new Derived2());
// error: The method apply(capture#1-of ? extends Base) in the type Function<capture#1-of ? extends Base,String> is not applicable for the arguments (Derived2)
You could, however, use generics to abstract over the argument type:
<T extends Base> invokeSafely(Function<T, String> function, T argument) {
function.apply(argument);
}
which would allow you to write
invokeSafely(serviceClass::method1, new Derived1());
invokeSafely(serviceClass::method2, new Derived2());
but not
invokeSafely(function, new Derived1()); // same error as before
CodePudding user response:
//Incompatible types: Base is not convertible to Derived1
Function<? extends Base,String> function1 = serviceClass::method1;
It's some weird quirk of type inference that I don't fully understand. It's inferring Function<Base,String>
from the left-hand side and trying to apply it to the right-hand-side.
You can fix it like this. The cast is safe:
Function<? extends Base,String> function1 = (Function<Derived1, String>) serviceClass::method1;
or this:
Function<Derived1, String> tmp = serviceClass::method1;
Function<? extends Base,String> function1 = tmp;
It's worth noting that you can never invoke this function (except with null
), since you have thrown away the knowledge of what type of argument it expects. ? extends Base
is something that extends Base (i.e. Derived1 or Derived2) but we don't know which.
//Incompatible types: Object is not convertible to Derived1
Function<?,String> function2 = serviceClass::method1;
Same issue as the first one, except it's inferring Object instead of Base.
//Incompatible types: Base is not convertible to Derived2
Function<? super Base,String> function3 = serviceClass::method2;
Derived1 or Derived2 are not superclasses of Base. They are subclasses. Only Object is a superclass of Base. method2
does not expect Object
.