получение свойства java bean неизвестного класса

Я хотел бы иметь возможность вызывать «getProgram» для объектов, у которых есть этот метод, не зная, к какому классу они принадлежат. Я знаю, что здесь мне следует использовать интерфейс, но я работаю с чужим кодом и не могу перепроектировать классы, с которыми работаю. Я думал, что BeanUtils.getProperty может мне помочь, но, похоже, он возвращает только строки. Есть ли что-то вроде Beanutils.getProperty, которое вернет литой объект? Или другой, более разумный способ работы с двумя похожими классами, которые не имеют общего интерфейса? спасибо, -Морган


person morgancodes    schedule 27.01.2009    source источник


Ответы (7)


Предположительно, у вас есть конечное число классов, реализующих этот метод, и вы можете ссылаться на них напрямую. Так что вам не нужно отражение. Отражение зло.

Скажем, у вас есть набор классов с методом:

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

Тогда вам просто нужны пары instanceof/cast. Вы можете поместить это в метод, чтобы вам нужно было сделать это только один раз.

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.
    }
}

В качестве альтернативы вы можете использовать адаптер:

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();
    }
    ...
}
person Tom Hawtin - tackline    schedule 27.01.2009
comment
это довольно просто и имеет смысл. Можете ли вы привести пример одного из зол отражения? - person morgancodes; 27.01.2009
comment
Кроме того, в языке OO вам не нужно использовать рекурсивный список if(instanceof) для вызова метода. - person Aaron Digulla; 27.01.2009
comment
нет, и они действительно должны быть одного класса или, по крайней мере, иметь общий интерфейс, но это вопрос дизайна вне моего контроля - person morgancodes; 27.01.2009

Используйте PropertyUtils (из apache commons-beanutils) вместо BeanUtils.

У него есть метод getProperty(Object bean, String name), который возвращает Object вместо String.

Дополнительную информацию см. в JavaDoc.

person johnmcase    schedule 27.01.2009

Просто используйте для этого отражение... в следующем примере показано, как это сделать на объектах, не имеющих общего интерфейса.

    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!");
    }
}
person tddmonkey    schedule 27.01.2009
comment
Существуют ли проблемы с производительностью, связанные с использованием Refection API? - person morgancodes; 27.01.2009
comment
Отражение примерно в 25 раз медленнее обычного вызова метода, поэтому кэшируйте объект метода по крайней мере, когда это возможно. - person Esko; 27.01.2009

См. Reflection API:

Используйте Class.getMethod() (или getMethods()), чтобы найти подходящий метод и вызвать его.

person Kevin    schedule 27.01.2009

Довольно простое решение: используйте делегирование, которое реализует интерфейс:

interface GetProgram
{
    String getProgram ();
}

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

Теперь вы можете использовать интерфейс в своем коде, не касаясь исходных классов.

Недостаток: это не работает, если вы A созданы где-то за пределами вашей досягаемости. Лучше всего работает, если A создается один раз под вашим контролем, и вы можете сразу же обернуть его.

person Aaron Digulla    schedule 27.01.2009

Чуть более короткая версия, если у вас 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);
}
person Peter Lawrey    schedule 27.01.2009

java.beans.Expression сделает это , если метод доступен в конкретном классе получателя.

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!");
    }
}
person Pete Kirkham    schedule 27.01.2009