Есть ли не исключительный способ доступа к getDeclaredMethod? - PullRequest
1 голос
/ 21 марта 2019

У меня есть два объекта, для которых мне может понадобиться вызвать методы, и я не буду знать, к какому из них он относится. Прямо сейчас мой основной рабочий процесс выглядит так:

Method method = null;
Target target = null;
try {
    method = first.getClass().getDeclaredMethod(methodName, typeParams);
    target = first;
} catch(NoSuchMethodException e) { 
    try {
        method = second.getClass().getDeclaredMethod(methodName, typeParams);
        target = second;
    } catch(NoSuchMethodException e1) {
        // they sent us a bad command, return 404-esque response
    }
}
method.invoke(target, arguments);

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

if(first.getClass().hasDeclaredMethod(methodName, typeParams)) {
    return first.getClass().getDeclaredMethod(methodName, typeParams).invoke(first, arguments); 
}
if(second.getClass().hasDeclaredMethod(methodName, typeParams)) {
    return second.getClass().getDeclaredMethod(methodName, typeParams).invoke(second, arguments); 
}
// they sent us a bad command, return 404-esque response

Какие варианты доступны для уменьшения зависимости от исключений таким образом?Я бы предпочел не писать «методы-обертки», так как они могут быть громоздкими и трудно определить, когда произошла ошибка или нет.

Ответы [ 2 ]

1 голос
/ 22 марта 2019

Если вы не хотите перехватывать исключения, вы должны реализовать поиск, например

public static Optional<Method> getMethod(Class<?> decl, String name, Class<?>... arg) {
    return Arrays.stream(decl.getDeclaredMethods())
        .filter(m -> name.equals(m.getName()) && Arrays.equals(m.getParameterTypes(), arg))
        .findAny();
}

, который вы можете использовать, например,

Optional<Method> m = getMethod(target.getClass(), methodName, typeParams);
if(!m.isPresent()) {
    target = second;
    m = getMethod(target.getClass(), methodName, typeParams);
}
if(m.isPresent()) try {
    m.get().invoke(target, args);
}
catch (IllegalAccessException|InvocationTargetException ex) {
    …
}

, хотя другие способы использования опциональнывозможны.

Вы можете испытать искушение сказать «подождите ... но это делает линейный поиск по всем объявленным методам», но никто никогда не обещал, что getDeclaredMethod(String, Class<?>...) делает что-то лучше, чем линейный поиск,и фактически, в широко используемой эталонной реализации, это не так.Поиск, который он выполняет, имеет ту же логику, что и приведенный выше, за исключением того, что в конце выдается исключение, если совпадение не найдено.

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

0 голосов
/ 21 марта 2019

Я бы выбрал собственный вспомогательный метод. Примерно так должно поступить:

static Optional<Object> invokeMethodIfPresent(Object target, String methodName, 
       Class<?>[] typeParams, Object[] arguments) {
    try {
        Method m = target.getClass().getDeclaredMethod(methodName, typeParams);
        return Optional.of(m.invoke(target, arguments));
    } catch (NoSuchMethodException e) {
        return Optional.empty();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

И это можно использовать относительно более чистым способом:

return Stream.of(first, second, etc.)
          .map(o -> invokeMethodIfPresent(o, methodName, typeParams, arguments))
          .filter(Optional::isPresent)
          .findFirst()
          .orElse(null);

Или используя несколько объектов:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...