java 8中的变量方法引用

时间:2016-08-22 13:52:09

标签: java functional-programming

我正在尝试使用变量创建方法引用,该变量保存来自对象的某些方法的方法名称:

SomeClass obj = new SomeClass();
String methodName = "someMethod";

我正在寻找精确创建obj::someMethod的方法,但为此使用变量methodName。有可能吗?

我知道如何从methodNameobj创建功能界面实例:

() -> {
    try {
        return obj.getClass().getMethod(methodName).invoke(obj);
    } catch (NoSuchMethodException | IllegalAccessException e) {
        return null;
    }
};

但我想知道这可以用更简短的方式完成。

2 个答案:

答案 0 :(得分:1)

这是一种非常罕见的情况 - Java8语法糖已经针对此进行了优化的可能性极小。特别是,通常的编译时类型检查不可能发生(请记住,方法引用只是遵循特定类型契约的匿名类的语法糖)。

如果这种模式在您的代码库中很常见(希望它不是!),您可以将它移动到静态实用程序方法,然后执行() -> Utils.invoke(obj, methodName)

答案 1 :(得分:1)

如果你力求简洁而不是表现,那么自Java 1.4以来就有ExpressionStatement

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可能更有效率,Java 8是运行时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);

这具有更高的创建复杂性,但随后执行的函数将具有与编译时方法引用相同的效率,因为不再存在技术差异。