Можно ли рассматривать Java MethodHandles как функции первого класса?

Java метод класс и 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 быстрее, чем метод отражения, но все же далеко не так быстро, как обычный вызов метода. Возможно, для вызова метода JVM может применить некоторые оптимизации, которые невозможны с дескрипторами.

4 ответов


помимо любых аргументов производительности, передавая методы или MethodHandles:

  • теряет времени компиляции безопасности:
    • количество аргументов;
    • типы аргументов;
    • существование самого метода (вы используете имя строки, чтобы найти его).
  • теряет все объявленные проверенные исключения;
  • делает автоматический рефакторинг более трудным.

также, как ваш код демонстрирует, что использование методов или 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);
    }
}

по общему признанию, это не так хорошо, как настоящий лямбда-синтаксис, но безопасность типов, преимущества рефакторинга и простота объяснения делают ввод этих пяти строк наименее худшим вариантом большую часть времени.


используйте отражение только тогда, когда вам действительно нужно. Как вы можете видеть, invoke бросает Throwable, что означает, что вы не получаете реальной хорошей обработки исключений. Также будут отброшены другие проверки времени компиляции.


метод класс имеет свое место, когда нужно использовать отражение, который намного медленнее, чем обычный вызов метода.

MethodHandler является частью JSR 292. Он предназначен для людей, которые реализуют динамические языки для JVM. Вы можете прочитать больше об этом здесь. Это также не означает замену регулярного вызова метода.


MethodHandles использовать отражение, поэтому они медленно и не легко refactorable. Кроме того, Java получит lambdas в версии 8, и это решит проблему функций первого класса в Java, поэтому я полагаю, что авторы библиотек предпочитают ждать Java 8, чем использовать какой-то обходной путь.