How can I obtain all declared method through MethodHandles.lookup()
? How can I obtain all declared fields?
What is difference betweeen MethodHandl
What is difference betweeen MethodHandle.invoke(), MethodHandle.invokeExact() and MethodHandle.invokeWithArguments()
Since I also struggled with this, I decided to revisit this question and write an example that shows exactly what the semantic differences between these methods are.
The major differences are:
invokeExact
only accepts exact arguments and return types, and does not accept arguments as an array. Calling e.g. method signature (Integer,Integer)Integer
with an int
argument is not allowed, but also calling it with an Object
argument is not allowed, even if the object is actually of type Integer - the compiletime type of the argument must be of class Integer, not the runtime instance:
Object arg = 1; Object[] args = {1,1};
Integer i = (Integer)handle.invokeExact(1,1); // OK
Object o = handle.invokeExact(arg,arg); // ERROR
handle.invokeExact(args); // ERROR
invoke
automatically converts argument types and return types and also converts between primitive types and the corresponding wrapper classes. But it does also not accept arguments as an array. E.g. for method signature (Integer,Integer)Integer
:
Object arg = 1; Object[] args = {1,1};
Integer i = (Integer)handle.invoke(1,1); // OK
Object o = handle.invoke(arg,arg); // OK!
o = handle.invoke(args); // ERROR
invokeWithArguments
removes all these restrictions and works very similar to Method#invoke
- you can also supply an array (or a java.util.List
) as an argument (but this flexibility comes with a huge performance penalty). E.g. for method signature (Integer,Integer)Integer
:
Object arg = 1; Object[] args = {1,1};
Integer i = (Integer)handle.invokeWithArguments(1,1); // OK
Object o = handle.invokeWithArguments(arg,arg); // OK
o = handle.invokeWithArguments(args); // OK!
Here a complete example:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.WrongMethodTypeException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class MethodHandleTest {
public static class TestClass{
public int test(int a, Integer b){
return a+b;
}
}
public static void main(String[] args) throws Throwable{
Method method = TestClass.class.getMethod("test", int.class, Integer.class);
MethodHandle handle = MethodHandles.lookup().unreflect(method).bindTo(new TestClass());
int arg_int = 1;
Integer argInteger = 1;
Object[] argArray = {1,1};
//----------------------------
// MethodHandle#invokeExact()
//----------------------------
// ONLY an exact invocation is allowed for invokeExact:
int result = (int) handle.invokeExact(arg_int, argInteger);
// inexact first argument type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (Integer,Integer)int"
Exception e = null;
try {
result = (int) handle.invokeExact(argInteger,argInteger);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
// inexact return type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Integer)Integer"
try {
result = (Integer) handle.invokeExact(arg_int,argInteger);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
// inexact return type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Integer)Object"
try {
Object o = handle.invokeExact(arg_int,argInteger);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
// "argObject" is ALSO NOT OK! - the compile time type of the argument must be of class Integer, not the runtime instance!
// -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Object)int"
Object argObject = argInteger;
try {
result = (int) handle.invokeExact(arg_int,argObject);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
// Array of the arguments NOT allowed -> throws WrongMethodTypeException - "expected (int,Integer)int but found (Object[])int"
try {
result = (int) handle.invokeExact(argArray);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
// But explicit cast of first or second argument is OK
result = (int) handle.invokeExact((int)argInteger,argInteger);
result = (int) handle.invokeExact(arg_int,(Integer)arg_int);
//-----------------------
// MethodHandle#invoke()
//-----------------------
// invoke() with exact types - OK -> actually calls invokeExact() behind the scenes
result = (int) handle.invoke(arg_int, argInteger);
// implicit conversion of inexact arguments and return type -> OK!
result = (Integer) handle.invoke(argInteger,argInteger);
// Object arguments or return type is OK!
Object o = handle.invoke(argObject,argObject);
// Array of the arguments NOT allowed -> throws WrongMethodTypeException - "cannot convert MethodHandle(int,Integer)int to (Object[])int"
try {
result = (int) handle.invoke(argArray);
} catch (WrongMethodTypeException ex) {
e = ex;
}
assert e != null;
e = null;
//------------------------------------
// MethodHandle#invokeWithArguments()
//------------------------------------
// invoke() with exact types - OK
result = (int) handle.invokeWithArguments(arg_int,arg_int);
// implicit conversion of inexact arguments and return type -> OK
result = (Integer) handle.invokeWithArguments(argInteger,argInteger);
// Object arguments or return type is OK!
o = handle.invoke(argObject,argObject);
// Array of the arguments -> OK
result = (int) handle.invokeWithArguments(argArray);
// List of arguments possible -> same as calling invokeWithArguments(list.toArray())
result = (int) handle.invokeWithArguments(Arrays.asList(argArray));
}
}
How can I obtain all declared method through MethodHandles.lookup()? How can I obtain all declared fields?
Think of java.lang.invoke as a (fast performing) extension to reflection (java.lang.reflect) - i.e. "invoke" classes are dependent upon "reflection" classes.
You obtain reference to all methods/constructors/fields via reflection (java.lang.Class and java.lang.reflect):
java.lang.Class<?> someClass = ...; // obtain a Class somehow
// Returns all constructors/methods/fields declared in class,
// whether public/protected/package/private,
// but does NOT include definitions from any ancestors:
java.lang.reflect.Constructor<?>[] declaredConstructors = someClass.getDeclaredConstructors();
java.lang.reflect.Method[] declaredMethods = someClass.getDeclaredMethods();
java.lang.reflect.Field[] declaredFields = someClass.getDeclaredFields();
// Returns all constructors/methods/fields declared as public members
// in the class AND all ancestors:
java.lang.reflect.Constructor<?>[] publicInheritedConstructors = someClass.getConstructors();
java.lang.reflect.Method[] publicInheritedMethods = someClass.getMethods();
java.lang.reflect.Field[] publicInheritedFields = someClass.getFields();
You convert them to MethodHandles via java.lang.invoke.MethodHandles.Lookup:
java.lang.invoke.MethodType mt;
java.lang.invoke.MethodHandle mh;
java.lang.invoke.MethodHandles.Lookup lookup = MethodHandles.lookup();
// process methods
for (java.lang.reflect.Method method: declaredMethods) {
mh = lookup.unreflect(method);
// can call mh.invokeExact (requiring first parameter to be the class'
// object instance upon which the method will be invoked, followed by
// the methodparameter types, with an exact match parameter and return
// types) or
// mh.invoke/invokeWithArguments (requiring first parameter to be the
// class' object instance upon which the method will be invoked,
// followed by the method parameter types, with compatible conversions
// performed on input/output types)
}
// process constructors
for (java.lang.reflect.Constructor<?> constructor: declaredConstructors) {
mh = lookup.unreflectConstructor(constructor);
// can call mh.invokeExact or
// mh.invoke/invokeWithArguments
}
// process field setters
for (java.lang.reflect.Field field: declaredFields) {
mh = lookup.unreflectSetter(field);
// can call mh.invokeExact or
// mh.invoke/invokeWithArguments
}
// process field getters
for (java.lang.reflect.Field field: declaredFields) {
mh = lookup.unreflectGetter(field);
// can call mh.invokeExact or
// mh.invoke/invokeWithArguments
}
You can determine if the signature of methods/constructors/fields via java.lang.reflect:
// If generics involved in method signature:
Type[] paramTypes = method.getGenericParameterTypes();
Type returnType = method.getGenericReturnType();
// Note: if Class is non-static inner class, first parameter of
// getGenericParameterTypes() is the enclosing class
// If no generics involved in method signature:
Class<?>[] paramTypes = declaredMethod.getParameterTypes();
Class<?> returnType = declaredMethod.getReturnType();
// Note: if Class is non-static inner class, first parameter of
// getParameterTypes() is the enclosing class
// Same method calls for declaredConstructor
You can determine if the methods/constructors/fields are static via java.lang.reflect:
int modifiers = method.getModifiers(); // same method for constructor/field
boolean isStatic = java.lang.Modifier.isStatic(modifiers);
What is difference betweeen MethodHandle.invoke(), MethodHandle.invokeExact() and MethodHandle.invokeWithArguments()?
MethodHandle
is for a non-static method, the first parameter provided to these methods is an instance of the Class
which declares the method. The method is invoked on this instance of the class (or on the Class itself for static methods). If the Class
is a non-static inner class, the second parameter is an instance of the enclosing/declaring class. The subsequent parameters are the method signature parameters, in order.invokeExact
does not do automatic compatible type conversion on input parameters. It requires parameter values (or parameter expressions) to be an exact type match to the method signature, with each parameter provided as a separate argument OR all arguments provided together as an array (signature: Object invokeExact(Object... args)
). invoke
requires the parameter values (or parameter expressions) to be type compatible match to the method signature - automatic type conversions are performed, with each parameter provided as a separate argument OR all arguments provided together as an array (signature: Object invoke(Object... args)) invokeWithArguments
requires the parameter values (or parameter expressions) to be type compatible match to the method signature - automatic type conversions are performed, with each parameter provided within a List (signature: Object invokeWithArguments(List<?> arguments)
) I will be appreciate for tutorial about using MethodHandle API for Java devloper
There's not much out there, unfortunately. You could try the following. Hopefully, I've given enough info above :^)
http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html
http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandles.Lookup.html
http://www.java7developer.com/blog/?p=191
http://www.oraclejavamagazine-digital.com/javamagazine/20130102?pg=52&search_term=methodhandle&doc_id=-1#pg50
http://www.amazon.com/Well-Grounded-Java-Developer-techniques-programming/dp/1617290068
As Balder said both calls invoke
and invokeExact
do not accept arguments passed in as an array. (However, they do accept array arguments.)
int[] args = {1,1};
// handle for Math.addExact(int, int)
Object o = handle.invokeExact(1,1); // OK
Object o = handle.invoke(1,1); // OK
Object o = handle.invokeExact(args); // ERROR
Object o = handle.invoke(args); // ERROR
The error is always a wrong type exception:
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(int, int)int to (Object[])int
So it does not unpack the array. But passing in an array where it is required works:
// handle for Arrays.sort(int[])
handle.invokeExact(args); // OK
handle.invoke(args); // OK
As Balder said, implementing the desired behavior with invokeWithArguments()
may incur a quite substantial overhead.
In order to get the desired behavior for unpacking argument lists as known from varargs, one has to turn the handle into a spreader:
// handle for Math.addExact(int, int)
handle = handle.asSpreader(int[].class, 2);
handle.invokeExact(args); // OK
handle.invoke(args); // OK
Of course, the same functionality as for explicit argument passing accounts again when the array is defined to be generic:
Object[] args = new Object[]{1,1};
// handle for Math.addExact(int, int)
handle = handle.asSpreader(int[].class, 2);
handle.invokeExact(args); // ERROR
handle.invoke(args); // OK
I have not conducted any performance comparison between the calls. For those interested, it is quite straight forward to extend this benchmark: http://rick-hightower.blogspot.de/2013/10/java-invoke-dynamic-examples-java-7.html
Essentially invokeWithArguments
does similar things but does so for every call:
public Object invokeWithArguments(Object... arguments) throws Throwable {
MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length);
return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments);
}
So creating a spreader once and reusing it, will most likely yield similar performance as invoke
and invokeExact
.