Reputation: 9765
I am dealing with some auto-generated legacy Java code, which has the same method name and return type, but does not implement any interface. I'd like to find some common way to handle the calls.
For example, I have a group of java classes, A1, A2, both contain a method, "getData()", getData method will return a set of another instances of classes, like B1, B2. Then in B1, B2, they all have method like "getName".
I need to call A.getData().getName(). Other than using Java reflection, is there any proper way to handle these scenarios?
class A1 {
B1 b;
B1 getData() {
return b;
}
}
class B1 {
String getName() {
return "myname in B1";
}
}
If B1, B2 implement some interface (eg. NameInterface) which has getName method, I could use Supplier like following:
A1 a1 = ...
A2 a2 = ...
doGetName(a1::getData);
doGetName(a2::getData);
void doGetName(Supplier<NameInterface> func) {
func.get().getName();
}
But unfortunately, B1, B2 are also auto-generated legacy code, I can't assign interface to them.
Could I define B1, B2 as supplier too? If it is possible, how to define it?
Thanks.
Upvotes: 1
Views: 815
Reputation: 86223
If I may declare your methods (getData
and getName
) public
, the following works for me. I am not using any supplier.
/**
* Requires that {@code a} has a public method getData that returns an object with a public getName method
*
* @param a
* @return a.getData().getName(); or null if the methods don’t exist
*/
public static String doGetName(Object a) {
try {
Class<?> aClass = a.getClass();
Method getDataMethod = aClass.getMethod("getData");
Object b = getDataMethod.invoke(a);
Class<?> bClass = b.getClass();
Method getNameMethod = bClass.getMethod("getName");
Object name = getNameMethod.invoke(b);
return name.toString();
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
System.out.println(e);
return null;
}
}
Call as for example
System.out.println(doGetName(new A1()));
Please modify to your needs.
If your methods aren’t public, you may use getDeclaredMethod()
instead of getMethod()
.
Upvotes: 2
Reputation: 7032
The only option I can see is to edit the A
classes to directly return the name of the wrapped data.
class A1 {
B1 b;
B1 getData() {
return b;
}
String getDataName() {
//Adding a null check here is probably smart, unless you know b != null
return getData().getName();
}
}
Because (you've implied that) each A
class to have a different (but concrete) wrapped data, you can access their getName()
methods for that specific type of B
. You still have to copy-paste that method into each A
because there's no common supertype of B1
, B2
, etc, but it's not the end of the world and much better than having to resort to Reflection.
From there we can add a common interface for A
s:
interface DataWrapper {
Object getData(); //Could change the return type to a common ancestor if
//the legacy code is ever refactored
String getDataName();
}
And have an abstract class A
implement that but leave the implementation to its subclasses:
abstract class A implements DataWrapper {
//Other A stuff
}
class A1 extends A {...}
class A2 extends A {...}
Or alternatively just put those methods into A
itself:
abstract class A {
Object getData(); //Could change the return type to a common ancestor if
//the legacy code is ever refactored
String getDataName();
//Other stuff.
}
class A1 extends A {...}
class A2 extends A {...}
And not need the interface at all.
Then there's no issue getting the name of the wrapped data.
A a = ...
String name = a.getDataName();
Upvotes: 1