Week 5 — What are method handles?

Question of the Week #5
What are method handles?
7 Replies
Eric McIntyre
Eric McIntyre2y ago
Method handles are objects representing methods allow low-level access to them (e.g. invoking). For creating MethodHandles, one needs to obtain a MethodHandles.Lookup which is responsible for finding the method handles first. Then, that Lookup object can be used for obtaining a MethodHandle for a method in a specific class (specified by a java.lang.Class-object), the name of the method and the signature which is represented by a MethodType-object. A MethodHandle-object can be created like the following:
MethodHandles.Lookup lookup = MethodHandles.publicLookup();//creates a MethodHandles.Lookup-object which can only be used for accessing public elements
Class<?> c = lookup.findClass("java.lang.String");//finds the class java.lang.String, String.class could be used instead
MethodType type = MethodType.methodType(int.class);//define a MethodType representing the signature, The method returns an int and doesn't have any parameters
MethodHandle handle = lookup.findVirtual(c,"length", type);//finds a non-static method called "length" in the class c (String) with the specified signature
int returnedValue = (int) handle.invoke("Hello World");//calls the method on the String object "Hello World"
System.out.println(returnedValue);//returns the result
MethodHandles.Lookup lookup = MethodHandles.publicLookup();//creates a MethodHandles.Lookup-object which can only be used for accessing public elements
Class<?> c = lookup.findClass("java.lang.String");//finds the class java.lang.String, String.class could be used instead
MethodType type = MethodType.methodType(int.class);//define a MethodType representing the signature, The method returns an int and doesn't have any parameters
MethodHandle handle = lookup.findVirtual(c,"length", type);//finds a non-static method called "length" in the class c (String) with the specified signature
int returnedValue = (int) handle.invoke("Hello World");//calls the method on the String object "Hello World"
System.out.println(returnedValue);//returns the result
Similarly, it is possible to obtain VarHandles for fields as well as it is possible to access private members. Assume the following class with a private field called s:
public class SomeClass{
private String s = "Hello";
public String getS(){
return s;
}
}
public class SomeClass{
private String s = "Hello";
public String getS(){
return s;
}
}
It is possible to access the field using VarHandles:
Class<?> c=SomeClass.class;//SomeClass as a Class<?> object
MethodHandles.Lookup parentLookup = MethodHandles.lookup();//get the default lookup object
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c,parentLookup);//get a Lookup object that can be used for accessing private members of the class c (SomeClass)
VarHandle handle = lookup.findVarHandle(c,"s",String.class);//get the variable handle of the variable name "s" of type String in the class
SomeClass o=new SomeClass();//create an object of type SomeClass
String s = (String)handle.get(o);//get the value of the field using the handle on the object created earlier
System.out.println(s);//print the value, in this case Hello
handle.set(o,"World");//sets the field to something else
System.out.println(o.getS());//check that the field has actually changed, prints World
Class<?> c=SomeClass.class;//SomeClass as a Class<?> object
MethodHandles.Lookup parentLookup = MethodHandles.lookup();//get the default lookup object
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c,parentLookup);//get a Lookup object that can be used for accessing private members of the class c (SomeClass)
VarHandle handle = lookup.findVarHandle(c,"s",String.class);//get the variable handle of the variable name "s" of type String in the class
SomeClass o=new SomeClass();//create an object of type SomeClass
String s = (String)handle.get(o);//get the value of the field using the handle on the object created earlier
System.out.println(s);//print the value, in this case Hello
handle.set(o,"World");//sets the field to something else
System.out.println(o.getS());//check that the field has actually changed, prints World
Accessing private members only works if the code using method handles/creating the lookup object has the required privileges (must have reflective access to the module it is accessing). Consider again a class and a subclass overriding a method:
public class SomeClass{
private String s = "Hello";
public String getS(){
return s;
}
}
public class OtherClass extends SomeClass{
@Override
public String getS(){
return "x";
}
}
public class SomeClass{
private String s = "Hello";
public String getS(){
return s;
}
}
public class OtherClass extends SomeClass{
@Override
public String getS(){
return "x";
}
}
MethodHandles can also be used to ignore runtime polymorphism using MethodHandles.Lookup#findSpecial / invokespecial. This requires a Lookup-object with access to private methods as well:
Class<?> c = SomeClass.class;
MethodHandles.Lookup parentLookup = MethodHandles.lookup();//get the default lookup object
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c,parentLookup);//get a Lookup object that can be used for accessing private members of the class
MethodType type = MethodType.methodType(String.class);//signature, returns String, no parameters
MethodHandle handle = lookup.findSpecial(c,"getS",type, SomeClass.class);//get the method handle but for invokespecial and bind it to SomeClass#getS specifically
SomeClass o = new OtherClass();
String s = (String)handle.invoke(o);//invoke the method, this invokes SomeClass#getS instead of OtherClass#getS even though o is an instance of SomeClass
System.out.println(s);//print the return type prints Hello
Class<?> c = SomeClass.class;
MethodHandles.Lookup parentLookup = MethodHandles.lookup();//get the default lookup object
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c,parentLookup);//get a Lookup object that can be used for accessing private members of the class
MethodType type = MethodType.methodType(String.class);//signature, returns String, no parameters
MethodHandle handle = lookup.findSpecial(c,"getS",type, SomeClass.class);//get the method handle but for invokespecial and bind it to SomeClass#getS specifically
SomeClass o = new OtherClass();
String s = (String)handle.invoke(o);//invoke the method, this invokes SomeClass#getS instead of OtherClass#getS even though o is an instance of SomeClass
System.out.println(s);//print the return type prints Hello
Eric McIntyre
Eric McIntyre2y ago
The Method Handles API is a newer, more modern, safer (e.g. it fails earlier if the types are invalid) and more powerful alternative to the Reflection API. It allows access to bytecode instructions like invokeinterface/invokevirtual/invokespecial/invokestatic/ via a Java API even without statically having a reference to the classes.
⭐ Submission from dan1st#7327
Eric McIntyre
Eric McIntyre2y ago
Method handles are an conceptual construct in the Java language to provide direct, type-safe access to fields and methods. They provide the ability to dynamically invoke methods and directly access fields, allowing for more dynamic code than using traditional reflection. Method handles can also be thought of to be similar to pointers to functions in low-level languages such as C and C++.
Eric McIntyre
Eric McIntyre2y ago
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; public class MethodHandlesExample {
public static void main(String[] args) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); // Define method type MethodType mt = MethodType.methodType(void.class); // Access any static method MethodHandles.Lookup staticLookup = MethodHandles.publicLookup(); MethodHandle mh0 = staticLookup.findStatic(MethodHandlesExample.class, "printMessage", mt); // Access private method MethodHandle mh1 = lookup.findSpecial(MethodHandlesExample.class, "printMessage", mt, this.getClass()); // Invoke the method handle mh0.invokeExact(); mh1.invokeExact(this); } final void printMessage() { System.out.println("The message is " Hello World! ""); } }
Submission from Arsh#9916
Eric McIntyre
Eric McIntyre2y ago
Method handles are a read-only meta-programming tool which works alongside the reflection API. They are references to methods as well as fields which is described in the official documentation as
A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution.
(https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/MethodHandle.html) We will take a simple example of method handles and go through every aspect step by step.
// create a lookup which we will use to find our handle later
var lookup = MethodHandles.publicLookup();

// create the type for which we want to find a handle
var type = MethodType.methodType(void.class, String[].class);

// find the handle using the lookup, our type and the name of the method
var handle = lookup.findStatic(MyApplication.class, "main", type);

// invoke our method with the String[] as an argument
handle.invokeExact(new String[] { "argument #1", "argument #2", "etc." } );
// create a lookup which we will use to find our handle later
var lookup = MethodHandles.publicLookup();

// create the type for which we want to find a handle
var type = MethodType.methodType(void.class, String[].class);

// find the handle using the lookup, our type and the name of the method
var handle = lookup.findStatic(MyApplication.class, "main", type);

// invoke our method with the String[] as an argument
handle.invokeExact(new String[] { "argument #1", "argument #2", "etc." } );
Here we can see that by creating a lookup, which acts as our way of finding the specified handle in the and, as well as the type of method we want we can scan a class for methods of the specified type with its name. After we have done this it is possible to invoke the method the handle refers to with invokeExact or invoke
Eric McIntyre
Eric McIntyre2y ago
Important differences to the reflection API are that a method handle is a direct reference or pointer to the underlying method and is a lot faster to invoke because of this. Additionally security checks are made upon creation and not invocation which also has a lot of advantages. Disadvantages are that the API is not as feature-rich as the reflection api
⭐ Submission from jade#9418
Eric McIntyre
Eric McIntyre2y ago
MethodHandles are the newer equivalent to the Method class from reflection. Basically a way to programmatically call another method.
class Abc {
public void abcd(String t) {
System.out.println("hi, "+t);
}
}
Abc abc = new Abc();
// reflection (old)
Method abcd = abc.getClass().getMethod("abcd", String.class);
abcd.invoke(abc, "world");

// methodhandles (new)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(void.class, String.class);
MethodHandle handle = lookup.findVirtual(abc.getClass(), "abcd", type);
handle.invoke(abc, "world");
class Abc {
public void abcd(String t) {
System.out.println("hi, "+t);
}
}
Abc abc = new Abc();
// reflection (old)
Method abcd = abc.getClass().getMethod("abcd", String.class);
abcd.invoke(abc, "world");

// methodhandles (new)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(void.class, String.class);
MethodHandle handle = lookup.findVirtual(abc.getClass(), "abcd", type);
handle.invoke(abc, "world");
Reflection was the old way of doing it, and it's still used, but methodhandles provide a ton of benefits that reflection just cant reach. MethodHandles work a lot closer to the actual jvm than reflection does, which can allow you to do some pretty funny things. Beyond that, there are other handles for constructors, fields, etc. This also means that methodhandles are a lot faster than reflection.
Submission from 0x150#3309
Want results from more Discord servers?
Add your server