【问题标题】:Can Java MethodHandles be considered the same as first class functions?Java MethodHandles 可以被认为与第一类函数相同吗?
【发布时间】:2011-12-13 02:27:09
【问题描述】:

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

(注意: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 可以应用一些句柄无法实现的优化。

【问题讨论】:

  • 他们不是很慢吗?
  • 我不得不说我还没有做任何基准测试。
  • 方法很少使用,因为它们使用反射并且比直接方法调用慢得多。 MethodHandles 很少使用,因为它们直到最近才存在。 (Java7)
  • 方法句柄比本机调用或反射快。该 API 主要用于链接 invokedynamic 调用站点。

标签: java java-7 higher-order-functions


【解决方案1】:

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

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

此外,正如您的代码所示,使用 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 语法好,但类型安全、重构的好处和易于解释使得输入这 5 行代码在很多时候成为最不糟糕的选择。

【讨论】:

  • 好吧,Guava 仍然是类型安全的,但它们仍然是匿名类。
【解决方案2】:

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

【讨论】:

  • 失去异常处理是一笔交易。虽然看起来很多 Java 人不喜欢检查异常。
【解决方案3】:

Method 类在需要使用反射时占有一席之地,这比常规方法调用要慢得多。

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

【讨论】:

  • 是的,还是很慢。我猜当 Groovy 实现它时,它会更快,但不如 Java 快。事实上,Groovy 正在转向静态类型系统。
【解决方案4】:

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

【讨论】:

  • 是的,可悲的是,它们仍然很慢。我想知道他们将如何使用 lambdas 解决性能损失。
  • CICE lambdas 提案仅仅是匿名内部类的语法快捷方式,因此它与构建简单对象和调用方法一样快。你不能再快了......其他提议在概念上更复杂,但应该是静态类型的,并且在 VM 管道方面或多或少等效。所以我猜 Java lambda 会比基于反射的机制快几个数量级。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 2019-02-13
  • 2020-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-30
相关资源
最近更新 更多