Общие методы Java приводятся к типу параметра во время выполнения, возможно ли это?

У меня есть метод, который выглядит так

 public static <T extends MyClass, X extends AnotherClass> List<T> (Class<T> aParameter, X anotherParameter)

Теперь, если AnotherClass является абстрактным классом, который НЕ имеет определенного getId, но каждый отдельный класс, расширяющий этот интерфейс, имеет. (Не спрашивайте меня, почему он разработан именно для этого, я не разрабатывал абстрактный класс, и мне не разрешено его изменять).

Как я могу сделать что-то подобное

anotherParameter.getId();

Я знаю, что должен привести его к классу, но затем мне нужно выполнить проверку экземпляра для каждого возможного класса, а затем выполнить его.

Итак, знайте, у меня есть что-то вроде:

if (anotherParameter instanceof SomeClass)
    ((SomeClass)anotherParameter).getId();  //This looks bad.

Можно ли применить это динамически? к любому другому параметру во время выполнения?.


person Oscar Gomez    schedule 15.09.2010    source источник


Ответы (5)


Можно ли модифицировать производные классы? Если это так, вы можете определить интерфейс для этого (синтаксис может быть неправильным):

public interface WithId {
    void getId();
}
...
public class MyDerivedClass1 extends AnotherClass implements WithId {
...
}
...
public class MyDerivedClass2 extends AnotherClass implements WithId {
...
}

а затем внутри вашего метода выполните:

...
if (anotherParameter instanceof WithId) {
 WithId withId = (WithId) anotherParameter;
 withId.getId();
}
...

Если вы можете изменить подпись своего метода, возможно, вы можете указать тип перекрестка:

public static <T extends MyClass, X extends AnotherClass & WithId> List<T> myMethod(Class<T> aParameter, X anotherParameter)

и тогда у вас будет getId(), доступный непосредственно внутри вашего метода.

person gpeche    schedule 15.09.2010
comment
Обратите внимание, что вместо реализации WithId вы можете 1) закодировать абстрактный класс AnotherClassWithId, предоставляющий abstract void getId() 2) сделать ваши производные классы расширяющими его и 3) сделать параметр вашего метода anotherParameter типом AnotherClassWithId. Это было бы проще и потребовало бы меньше изменений в вашем коде, но основная идея та же самая. - person gpeche; 15.09.2010

Я бы сказал нет, так как из-за стирания типа X на самом деле просто Object во время выполнения. Вы можете попробовать сделать что-то с отражением, чтобы проверить, имеет ли другой параметр getId(), и если да, то вызовите его.

person takteek    schedule 15.09.2010

Вы можете использовать отражение для вызова метода во время выполнения, если он существует.

try {
    Method m = anotherParameter.getClass().getMethod("getId", null);
    Object result = m.invoke(anotherParameter, null);
}
catch (NoSuchMethodException e) {
   // ... and several other exceptions need to be caught
}
person Stephen P    schedule 15.09.2010

Концепция приведения чего-то во время выполнения на самом деле не имеет смысла, поскольку у вас есть экземпляр, и он может сказать вам, какой это класс. Вам нужно будет использовать отражение, например, используя Introspector класс.

private Integer getId(final X anotherParameter) {
    final BeanInfo beanInfo = Introspector.getBeanInfo(anotherParameter.getClass());
    for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) {
        final Method method = methodDescriptor.getMethod();
        if ("getId".equals(method.getName())
                && method.getParameterTypes().length == 0) {
            return (Integer) method.invoke(anotherParameter);
        }
    }
    return null;
}
person Jon Freedman    schedule 15.09.2010

Как уже говорили другие, отражение является единственным практическим решением этой проблемы, но я бы немного улучшил его, кэшируя метаданные отражения (возможно, карту с ключом class+methodName), поскольку эта часть отражения не совсем дешевая. . Вы не можете помочь части "invoke()".

person David M. Karr    schedule 15.09.2010