是否有非例外的方式来访问getDeclaredMethod?

时间:2019-03-21 17:31:20

标签: java reflection

我可能需要调用两个对象,而我不知道它属于哪个对象。现在,我的基本工作流程是:

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 个答案:

答案 0 :(得分:1)

当您不想捕获异常时,必须执行搜索,即

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();
}

您可以使用like

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<?>...)会比线性搜索做得更好,事实上,在广泛使用的参考实现中,事实并非如此。它进行的搜索与上面显示的逻辑具有相同的逻辑,除了在找不到匹配项的情况下在末尾引发异常之外。

即使这样做,例如通过散列查找,创建新异常的成本可能会超过通过有限数量的已声明方法进行线性搜索的成本。

答案 1 :(得分:0)

我会寻求自定义帮助方法。这样的事情应该做:

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);

或使用多个对象: