Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Java MethodHandles be considered the same as first class functions?

Java Method Class and Java 7's MethodHandle Class both refer to objects that are associated to methods, but still they are rarely used and when a function needs to be passed to another, it is preferred to use an anonymous class that implements an interface that contains one method.

(Note: MethodHandles are supposed to be faster than the old Methods.)

Why aren't these constructs used more often to pass functions to functions? Is it because they are still verbose?

Code example:

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));
}
}

UPDATE

After doing some benchmarking, I noticed that MethodHandle is faster than Reflection's method, but still nowhere near as fast as a regular method call. Maybe for the method call the JVM can apply some optimizations that are not possible with the handles.

like image 935
Luciano Avatar asked Dec 13 '11 02:12

Luciano


1 Answers

Aside from any performance arguments, passing around Methods or MethodHandles:

  • loses any compile-time safety:
    • number of arguments;
    • types of arguments;
    • the existence of the method itself (you use a string name to find it).
  • loses any declared checked exceptions;
  • makes automated refactoring more difficult.

Also, as your code demonstrates, using Methods or MethodHandles doesn't particularly cut down on the amount of code required to declare an object approximating a first-class function:

// 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);
    }
}

Admittedly it's not as nice as a real lambda syntax, but the type-safety, refactoring benefits and ease of explanation make typing out those five lines the least-worst option much of the time.

like image 98
Dan Vinton Avatar answered Nov 05 '22 20:11

Dan Vinton