9

Java方法类和 Java 7 的MethodHandle类都指与方法相关联的对象,但它们仍然很少使用,当需要将函数传递给另一个函数时,最好使用实现包含一个接口的匿名类方法。

(注意:MethodHandles 应该比旧的方法更快。)

为什么不经常使用这些构造来将函数传递给函数?是因为它们仍然很冗长吗?

代码示例:

public final class HigherOrder {

public static final List<?> map(final List<?> list, final MethodHandle mh) throws Throwable {
    if (list == null) return null;
    List<Object> ret = new ArrayList<>(list.size());
    for (Object element : list) {
        ret.add(mh.invoke(element));
    }
    return ret;
}

public static final Object reduce(final List<?> list, final MethodHandle mh) throws Throwable {
    if (list == null) return null;
    Object tmp = list.get(0);
    for (int i = 1; i < list.size(); i++) {
        tmp = mh.invoke(tmp, list.get(i));
    }
    return tmp;
}

public static final Integer doubleNumber(final Integer number) {
    return number * 2;
}

public static final Integer sum(final Integer number1, final Integer number2) {
    return number1 + number2;
}

public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle doubleNumber = lookup.unreflect(HigherOrder.class.getMethod("doubleNumber", Integer.class));
    MethodHandle sum = lookup.findStatic(HigherOrder.class, "sum", MethodType.methodType(Integer.class, Integer.class, Integer.class));

    List<?> list = Arrays.asList(1, 2, 3, 4, 5);
    System.out.println(list);
    list = map(list, doubleNumber);
    System.out.println(list);
    System.out.println(reduce(list, sum));
}
}

更新

在进行了一些基准测试后,我注意到 MethodHandle 比 Reflection 的方法快,但仍远不及常规方法调用。也许对于方法调用,JVM 可以应用一些句柄无法实现的优化。

4

4 回答 4

3

除了任何性能参数,传递方法或方法句柄:

  • 失去任何编译时安全性:
    • 参数数量;
    • 论点的类型;
    • 方法本身的存在(您使用字符串名称来查找它)。
  • 丢失任何声明的检查异常;
  • 使自动化重构更加困难。

此外,正如您的代码所演示的,使用 Methods 或 MethodHandles 并没有特别减少声明一个近似于一流函数的对象所需的代码量:

// you could even use the Function types from Guava...
interface Func<In, Out> {
    Out apply(In in);
}

// create a new "function" object in 5 lines of code:
Func<String, Integer> parseInt = new Func<String, Integer> {
    public Integer apply(String in) {
        return Integer.parseInt(in);
    }
}

诚然,它不如真正的 lambda 语法好,但类型安全、重构的好处和易于解释使得输入这五行代码在很多时候成为最不糟糕的选择。

于 2011-12-13T10:55:42.937 回答
0

仅在您真正需要时使用反射。正如您所看到的,invoke throws Throwable 这意味着您没有得到真正好的异常处理。其他编译时检查也将被丢弃。

于 2011-12-13T09:31:47.783 回答
0

当需要使用反射时,方法类有它的位置,这比常规方法调用要慢得多。

MethodHandlerJSR 292的一部分。它针对为 JVM 实现动态语言的人。你可以在这里阅读更多关于它的信息。这也不意味着要替换常规方法调用。

于 2011-12-13T11:00:49.743 回答
0

MethodHandles 使用反射,所以它们很慢并且不容易重构。此外,Java 将在版本 8 中获得 lambdas,这将解决 Java 中的第一类函数问题,所以我认为库编写者更喜欢等待 Java 8 而不是使用某种解决方法。

于 2011-12-13T11:02:17.900 回答