如何从代码中的代表语言功能的特定实例中获取以下类型的对象:
理想情况下,提供方法将是获取方法或获取方法本身的调用者。
请注意,我在SO上找到了两种答案:
无论是将参数提供给名称不唯一的特定方法都有帮助。
特别考虑类似问题this answer和this one如何掩盖所选方法的选择。
现在,存在Class.getMethod(String, Class<?>... types)
,但似乎没有人可以从现有方法定义自动生成“types”参数?
用例:
public class MyAssertions
{
private static final String DEFAULT_MESSAGE_NOT_NULL = "Parameter must not be null: ";
/* Ideal. */
public static void notNull(Object ref, Parameter param)
{
if(ref == null)
throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL + param.getName());
}
/* Still okay. */
public static void notNull(Object ref, Executable caller, int param)
{
if(ref == null)
throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL
+ caller.getParameters()[param].getName());
}
/* Hell no! */
public static void notNull(Object ref, Class<?> caller, String method, Object[] paramTypes, int param)
{
if(ref == null)
throw new IllegalArgumentException(DEFAULT_MESSAGE_NOT_NULL
+ caller.getMethod(method, paramTypes)
.getParameters()[param].getName());
}
}
答案 0 :(得分:1)
您可以使用字节码实用程序来获取字节码信息。然后使用StackTraceElement
中的行号信息获取method
。
这里我使用javassist。
private static Optional<Method> get(StackTraceElement e) throws NotFoundException, ClassNotFoundException {
Class<?> clz = Class.forName(e.getClassName());
int line = e.getLineNumber();
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(clz.getName());
return Observable.fromArray(cc.getDeclaredMethods())
.sorted(Comparator.comparing(m -> m.getMethodInfo().getLineNumber(0)))
.filter(m -> m.getMethodInfo().getLineNumber(0) <= line)
.map(Optional::of)
.blockingLast(Optional.empty())
.map(m -> uncheck(() -> clz.getDeclaredMethod(m.getName(),
Arrays.stream(m.getParameterTypes()).map(c -> uncheck(() -> nameToClass(c.getName()))).toArray(Class[]::new))));
}
private static Class<?> nameToClass(String name) throws ClassNotFoundException {
switch (name) {
case "int":
return int.class;
case "short":
return short.class;
case "long":
return long.class;
case "double":
return double.class;
case "float":
return float.class;
case "boolean":
return boolean.class;
case "char":
return char.class;
case "byte":
return byte.class;
case "void":
return void.class;
}
if (name.endsWith("[]")) {
return Array.newInstance(nameToClass(name.substring(0, name.length() - 2)), 0).getClass();
}
return Class.forName(name);
}
uncheck
是我的实用程序只忽略lambda中的异常,你可以简单地使用try-catch。 see source here
然后我们测试我们的代码
public static void main(String[] args) throws Exception {
// test normal
System.out.println(get(new Exception().getStackTrace()[0]));
// test lambda
Runnable r = () -> System.out.println(uncheck(() -> get(new Exception().getStackTrace()[0])));
r.run();
// test function
testHere(1);
}
private static void testHere(int i) throws Exception {
System.out.println(get(new Exception().getStackTrace()[0]));
}
和输出
Optional[public static void xdean.stackoverflow.java.reflection.Q44563354.main(java.lang.String[]) throws java.lang.Exception]
Optional[private static java.util.Optional xdean.stackoverflow.java.reflection.Q44563354.lambda$1() throws java.lang.Exception]
Optional[private static void xdean.stackoverflow.java.reflection.Q44563354.testHere(int) throws java.lang.Exception]
但请注意,此方法仅适用于类在classpath中具有字节码。如果您使用动态代理,它就无法运行。
查找完整的示例代码here
编辑:您可以过滤合成和桥接方法以获取实际方法。