tags:

views:

440

answers:

7

Hello, I'd like to be able to call "getProgram" on objects which have that method, without knowing which class they belong to. I know I should use an interface here, but I'm working with someone else's code and can't redesign the classes I'm working with. I thought BeanUtils.getProperty might help me, but it seems it only returns strings. Is there something like Beanutils.getProperty that will return a cast-able object? Or another, smarter way to work with two similar classes that don't share an interface? thanks, -Morgan

A: 

See the Reflection API:

Use Class.getMethod() (or getMethods()) to find the appropriate method and invoke it.

Kevin
+2  A: 

Just use reflection for this... the following example shows how to do it on objects that have no common interface.

    public static void main(String[] args) throws Exception {
 doSomething(new A());
 doSomething(new B());
}

private static void doSomething(Object object) throws Exception {
 Method m = object.getClass().getMethod("doSomething", (Class[])null);
 m.invoke(object, (Object[])null);
}

private static class A {
 public void doSomething() {
  System.out.println("I'm doing it already!");
 }
}

private static class B {
 public void doSomething() {
  System.out.println("I'm doing it too!");
 }
}
MrWiggles
thanks for the code, Mr. Wiggles. Looks good.
morgancodes
Are there performance issues involved in using the refection api?
morgancodes
Reflection is about 25x slower than normal method invocation so cache the Method object at least whenever you can.
Esko
A: 

java.beans.Expression will do that, as long as the method is accessible in the concrete class of the receiver.

public static void main(String[] args) throws Exception {
    new Expression(new A(), "doSomething", null).getValue();
    new Expression(new B(), "doSomething", null).getValue();
}

public static class A {
    public void doSomething() {
            System.out.println("I'm doing it already!");
    }
}

public static class B {
    public void doSomething() {
            System.out.println("I'm doing it too!");
    }
}
Pete Kirkham
+2  A: 

Use PropertyUtils (from apache commons-beanutils) instead of BeanUtils.

It has a getProperty(Object bean, String name) method that returns an Object instead of a String.

See the JavaDoc for more information.

John Case
A: 

Presumably you have a finite number of classes implementing this method, and you can link to them directly. So you don't need reflection. Reflection is evil.

Say you have a set of classes with the method:

public class LibA { public Program getProgram() { return program; } ... };
public class LibB { public Program getProgram() { return program; } ... };
...

Then you just need instanceof/cast pairs. You can put this in a method so that you only need to do it once.

public static Program getProgram(Object obj) {
    if        (obj instanceof LibA) {
        return              ((LibA)obj).getProgram();
    } else if (obj instanceof LibB) {
        return              ((LibB)obj).getProgram();
    } else {
        throw new IllegalArgumentException(obj+" doesn't have a known getProgram");
            // Or an appropriate application exception.
    }
}

Alternatively you might want to use an adapter:

public interface ProgramContainer { 
    Program getProgram();
    ...
}

public class LibAContainer implements ProgramContainer {
    private final LibA libA;
    public LibAContainer(LibA libA) {
        this.libA = libA;
    }
    public Program getProgram() {
        return libA.getProgram();
    }
    ...
}
Tom Hawtin - tackline
that's pretty straightforward and makes sense. Can you give me an example of one of reflection's evils?
morgancodes
Reflection is not evil :) The API just has many issues.
Aaron Digulla
Also, in an OO language, you should not need to use a recursive list of if(instanceof) to call a method.
Aaron Digulla
no, and these really should be the same class, or at least share an interface, but it's a design issue beyond my control
morgancodes
A: 

A pretty simple solution: Use delegation which implements the interface:

interface GetProgram
{
    String getProgram ();
}

class AWrapper implements GetProgram
{
    A a;
    public AWrapper (A a) { this.a = a;
    String getProgram () { return a.getProgram(); }
}

Now you can use the interface in your code without touching the original classes.

Drawback: This doesn't work well if you A is created somewhere outside of your reach. It works best if A is created once under your control and you can wrap it immediately.

Aaron Digulla
A: 

A slightly shorter version if you have Java 5+

public static void main(String[] args) throws Exception {
    System.out.println(invoke("toString", new A());
    System.out.println(invoke("toString", new B());
}

private static <R> R invoke(Object object, String methodName) throws Exception {
    return (R) object.getClass().getMethod(methodName).invoke(object);
}
Peter Lawrey