BlueDolphin
BlueDolphin

Reputation: 9765

How to handle multiple level of Java Supplier calls?

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

Answers (2)

Anonymous
Anonymous

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

Mshnik
Mshnik

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 As:

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

Related Questions