6

How do I get the MethodHandle for an array constructor like int[]::new?

This doesn't work:

public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.publicLookup();
    MethodHandle mh = lookup.findConstructor(int[].class, MethodType.methodType(void.class, int.class));
    System.out.println(mh);
    System.out.println(mh.invoke());
}

It results in this:

Exception in thread "main" java.lang.NoSuchMethodException: no such constructor: [I.<init>(int)void/newInvokeSpecial
    at java.lang.invoke.MemberName.makeAccessException(MemberName.java:871)
    at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:990)
    at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1382)
    at java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:920)
    at xx.main(xx.java:11)
Caused by: java.lang.NoSuchMethodError: java.lang.Object.<init>(I)V
    at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
    at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:962)
    at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:987)
    ... 3 more

Nor does this:

public static void main(String[] args) throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.publicLookup();
    MethodHandle mh = lookup.findConstructor(int[].class, MethodType.methodType(void.class));
    System.out.println(mh);
    System.out.println(mh.invoke());
}

It seems to find the constructor for Object instead:

MethodHandle()Object
java.lang.Object@36baf30c
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
Archie
  • 4,959
  • 1
  • 30
  • 36

2 Answers2

5

As I know int[].class has no constructor, so it's not available via reflection at least.

Instead, you can try to get MethodHandle on Array's factory method:

MethodHandle mh = lookup.findStatic(Array.class, "newInstance",
                             MethodType.methodType(Object.class, Class.class, int.class));

and create an array by calling it.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
ivanenok
  • 594
  • 5
  • 9
  • 2
    Just checked that `int[]::new` generates in bytecode synthetic method like `Object lambda$MR$new$new$4ffde7b3$1(int len) { return new int[len]; }`. – Tagir Valeev Mar 18 '16 at 04:43
  • In the context of the question, you surely want to chain a `.bindTo(int.class) .asType(MethodType.methodType(int[].class, int.class))` to get the desired handle representing an `int[]::new` logic, i.e. you can use it like `int[] array = (int[])mh.invokeExact(42);` then – Holger Mar 18 '16 at 12:00
  • Thanks to all. This worked for me: `MethodHandles.insertArguments(lookup.findStatic(Array.class, "newInstance", MethodType.methodType(Object.class, Class.class, int.class)), 0, int.class)` – Archie Mar 25 '16 at 22:26
1

Seems that @MaximSIvanov is right: there's no built-in way to get such method handle. However nothing stops you from creating a special method for this purpose and provide a handle to this method:

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;

public class ArrayMethodHandles {
    private static int[] makeIntArray(int size) {
        return new int[size];
    }

    public static MethodHandle createIntArray() {
        try {
            return MethodHandles.lookup().findStatic(ArrayMethodHandles.class, 
                "makeIntArray", MethodType.methodType(int[].class, int.class));
        } catch (NoSuchMethodException | IllegalAccessException e) {
            throw new InternalError();
        }
    }

    public static void main(String[] args) throws Throwable {
        MethodHandle mh = createIntArray();
        int[] array = (int[])mh.invokeExact(10);
        System.out.println(Arrays.toString(array));
        // prints [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    }
}

Something similar is actually performed by java compiler when you compile int[]::new method reference: helper private method is created. You may check by compiling the following class:

import java.util.function.*;

public class Test { 
    IntFunction<int[]> fn = int[]::new;
}

Running javap -p -c Test you will see that helper private method is generated and linked as MethodHandle to invokedynamic:

private static java.lang.Object lambda$MR$new$new$4ffde7b3$1(int);
    Code:
       0: iload_0
       1: newarray       int
       3: areturn
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334