s (char,char)String mt = MethodType.methodType(String.class, char.class, char.class); mh = lookup.findVirtual(String.class, "replace", mt); s = (String) mh.invokeExact("daddy",'d','n'); // invokeExact(Ljava/lang/String;CC)Ljava/lang/String; assertEquals(s, "nanny"); // weakly typed invocation (using MHs.invoke) s = (String) mh.invokeWithArguments("sappy", 'p', 'v'); assertEquals(s, "savvy"); // mt is (Object[])List mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); assert(mh.isVarargsCollector()); x = mh.invoke("one", "two"); // invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList("one","two")); // mt is (Object,Object,Object)Object mt = MethodType.genericMethodType(3); mh = mh.asType(mt); x = mh.invokeExact((Object)1, (Object)2, (Object)3); // invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; assertEquals(x, java.util.Arrays.asList(1,2,3)); // mt is ()int mt = MethodType.methodType(int.class); mh = lookup.findVirtual(java.util.List.class, "size", mt); i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3)); // invokeExact(Ljava/util/List;)I assert(i == 3); mt = MethodType.methodType(void.class, String.class); mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt); mh.invokeExact(System.out, "Hello, world."); // invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V} Each of the above calls to {@code invokeExact} or plain {@code invoke}generates a single invokevirtual instruction with the symbolic type descriptor indicated in the following comment. In these examples, the helper method {@code assertEquals} is assumed tobe a method which calls {@link java.util.Objects#equals(Object,Object) Objects.equals}on its arguments, and asserts that the result is true.
Exceptions
The methods {@code invokeExact} and {@code invoke} are declaredto throw {@link java.lang.Throwable Throwable}, which is to say that there is no static restriction on what a method handle can throw. Since the JVM does not distinguish between checked and unchecked exceptions (other than by their class, of course), there is no particular effect on bytecode shape from ascribing checked exceptions to method handle invocations. But in Java source code, methods which perform method handle calls must either explicitly throw {@code Throwable}, or else must catch all throwables locally, rethrowing only those which are legal in the context, and wrapping ones which are illegal.
Signature polymorphism
The unusual compilation and linkage behavior of {@code invokeExact} and plain {@code invoke}is referenced by the term
signature polymorphism. As defined in the Java Language Specification, a signature polymorphic method is one which can operate with any of a wide range of call signatures and return types.
In source code, a call to a signature polymorphic method will compile, regardless of the requested symbolic type descriptor. As usual, the Java compiler emits an {@code invokevirtual}instruction with the given symbolic type descriptor against the named method. The unusual part is that the symbolic type descriptor is derived from the actual argument and return types, not from the method declaration.
When the JVM processes bytecode containing signature polymorphic calls, it will successfully link any such call, regardless of its symbolic type descriptor. (In order to retain type safety, the JVM will guard such calls with suitable dynamic type checks, as described elsewhere.)
Bytecode generators, including the compiler back end, are required to emit untransformed symbolic type descriptors for these methods. Tools which determine symbolic linkage are required to accept such untransformed descriptors, without reporting linkage errors.
Interoperation between method handles and the Core Reflection API
Using factory methods in the {@link java.lang.invoke.MethodHandles.Lookup Lookup} API,any class member represented by a Core Reflection API object can be converted to a behaviorally equivalent method handle. For example, a reflective {@link java.lang.reflect.Method Method} canbe converted to a method handle using {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}. The resulting method handles generally provide more direct and efficient access to the underlying class members.
As a special case, when the Core Reflection API is used to view the signature polymorphic methods {@code invokeExact} or plain {@code invoke} in this class,they appear as ordinary non-polymorphic methods. Their reflective appearance, as viewed by {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod}, is unaffected by their special status in this API. For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers}will report exactly those modifier bits required for any similarly declared method, including in this case {@code native} and {@code varargs} bits.
As with any reflected method, these methods (when reflected) may be invoked via {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}. However, such reflective calls do not result in method handle invocations. Such a call, if passed the required argument (a single one, of type {@code Object[]}), will ignore the argument and will throw an {@code UnsupportedOperationException}.
Since {@code invokevirtual} instructions can nativelyinvoke method handles under any symbolic type descriptor, this reflective view conflicts with the normal presentation of these methods via bytecodes. Thus, these two native methods, when reflectively viewed by {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
In order to obtain an invoker method for a particular type descriptor, use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker}, or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}. The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}API is also able to return a method handle to call {@code invokeExact} or plain {@code invoke}, for any specified type descriptor .
Interoperation between method handles and Java generics
A method handle can be obtained on a method, constructor, or field which is declared with Java generic types. As with the Core Reflection API, the type of the method handle will constructed from the erasure of the source-level type. When a method handle is invoked, the types of its arguments or the return value cast type may be generic types or type instances. If this occurs, the compiler will replace those types by their erasures when it constructs the symbolic type descriptor for the {@code invokevirtual} instruction.
Method handles do not represent their function-like types in terms of Java parameterized (generic) types, because there are three mismatches between function-like types and parameterized Java types.
- Method types range over all possible arities, from no arguments to up to the maximum number of allowed arguments. Generics are not variadic, and so cannot represent this.
- Method types can specify arguments of primitive types, which Java generic types cannot range over.
- Higher order functions over method handles (combinators) are often generic across a wide range of function types, including those of multiple arities. It is impossible to represent such genericity with a Java type parameter.
Arity limits
The JVM imposes on all methods and constructors of any kind an absolute limit of 255 stacked arguments. This limit can appear more restrictive in certain cases:
- A {@code long} or {@code double} argument counts (for purposes of arity limits) as two argument slots.
- A non-static method consumes an extra argument for the object on which the method is called.
- A constructor consumes an extra argument for the object which is being constructed.
- Since a method handle’s {@code invoke} method (or other signature-polymorphic method) is non-virtual,it consumes an extra argument for the method handle itself, in addition to any non-virtual receiver object.
These limits imply that certain method handles cannot be created, solely because of the JVM limit on stacked arguments. For example, if a static JVM method accepts exactly 255 arguments, a method handle cannot be created for it. Attempts to create method handles with impossible method types lead to an {@link IllegalArgumentException}. In particular, a method handle’s type must not have an arity of the exact maximum 255.
@see MethodType
@see MethodHandles
@author John Rose, JSR 292 EG