我试图用变量创建一个方法引用,该变量保存来自对象的某些方法的方法名:
SomeClass obj = new SomeClass();
String methodName = "someMethod";
我正在寻找一种方法来创建精确的obj::someMethod
,但是为此使用变量methodName。有可能吗?
我知道如何从methodName
和obj
创建函数接口实例。
() -> {
try {
return obj.getClass().getMethod(methodName).invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException e) {
return null;
}
};
但我想知道这是否可以用更快的方式来完成。
发布于 2016-08-22 06:25:11
这是一个非常罕见的情况-- Java8语法糖不太可能被优化过。特别是,通常的编译时类型检查是不可能发生的(请记住,方法引用只是遵循特定类型契约的匿名类的语法糖)。
如果这个模式在您的代码库中很常见(希望它不是!),您可以将它移到静态实用程序方法中,然后执行() -> Utils.invoke(obj, methodName)
。
发布于 2016-08-23 05:39:25
如果您力求简洁而不是性能,那么Java1.4之后就会出现Expression
和Statement
。
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
try {
Object result=new Expression(obj, methodName, arg).getValue();
new Statement(System.out, "println", new Object[]{ result }).execute();
} catch (Exception ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
}
但是,如果您想在不允许检查异常的标准函数接口的上下文中使用它们,则异常处理将主导源代码。
即使在Java 7下,也可以将反射获取的方法绑定到功能接口:
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
Supplier<String> s;
Consumer<String> c;
try {
MethodHandle mh=MethodHandles.insertArguments(
MethodHandles.lookup().bind(obj, methodName,
MethodType.methodType(String.class, int.class, int.class)),
0, arg);
s = MethodHandleProxies.asInterfaceInstance(Supplier.class, mh);
mh=MethodHandles.lookup().bind(System.out, "println",
MethodType.methodType(void.class, String.class));
c = MethodHandleProxies.asInterfaceInstance(Consumer.class, mh);
} catch(NoSuchMethodException | IllegalAccessException ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
return;
}
String result=s.get();
c.accept(result);
这并不短,但避免在每个函数计算中执行反射查找。
潜在的更有效的方法是使用Java 8中引入的LambdaMetafactory
,它是运行时lambda表达式和方法引用的后端。
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
Supplier<String> s;
Consumer<String> c;
try {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh=lookup.findVirtual(String.class, methodName,
MethodType.methodType(String.class, int.class, int.class));
s = (Supplier<String>)LambdaMetafactory.metafactory(lookup, "get",
mh.type().changeReturnType(Supplier.class),
MethodType.methodType(Object.class), mh, MethodType.methodType(String.class))
.getTarget().bindTo(obj).invokeWithArguments(arg);
mh=MethodHandles.lookup().findVirtual(PrintStream.class, "println",
MethodType.methodType(void.class, String.class));
c = (Consumer<String>)LambdaMetafactory.metafactory(lookup, "accept",
MethodType.methodType(Consumer.class, PrintStream.class),
MethodType.methodType(void.class, Object.class), mh,
MethodType.methodType(void.class, String.class))
.getTarget().bindTo(System.out).invokeExact();
} catch(Throwable ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
return;
}
String result=s.get();
c.accept(result);
这有一个更高的创建复杂性,但随后的函数执行将具有与编译时方法引用相同的效率,因为不再存在技术上的差异。
https://stackoverflow.com/questions/39081366
复制相似问题