Navigation
Recherche
|
Better than reflection: Using method handles and variable handles in Java
vendredi 30 août 2024, 11:01 , par InfoWorld
One of the things that distinguishes a veteran Java developer is familiarity with reflection and its modern alternatives. Reflection gives you superpowers, but it is cumbersome, error-prone, and a performance bottleneck. Modern Java is working to replace reflection with standardized options, including MethodHandle and VarHandle. Like reflection, these classes give you the ability to access methods and fields on objects but in a cleaner API.
The power of handles As the names imply, both MethodHandle and VarHandle give you “handles,” which are variables to reference the meta-properties of an object. These handles give you the power to deal with methods and fields directly. They are special variables that refer to parts of the runtime environment otherwise hidden from your code. The starting point for these capabilities is the various lookup methods on MethodHandle, which offer a modern way to programmatically find the metadata for classes. This is similar to the old reflection API’s methods like getDeclaredMethod, but with more structure and safety. Once you have the handle for the class metadata, you can use MethodHandle and VarHandle to programmatically make calls against the method and fields that exist on a class instance. Under the hood, the JVM manages these approaches, which generally yields better performance than you would get with reflection. Method and variable handles vs. Java reflection To truly understand MethodHandles and VarHandles—what they do and why they are useful—it’s helpful to know a few things about reflection in Java. This will help you understand why reflection has evolved into these newer APIs. If you already know about reflection, the gist will be clear. If not, some examples will help illustrate. The fundamental question is: What need do these technologies—reflection, method handles, variable handles—fulfill? Why, when we can simply instantiate an object, call its public methods, and access its public members, would we do these things programmatically instead? In many cases, you actually can’t access what you need via public methods, so you have to go around the normal routes. This mostly happens when you are writing something like framework code that operates against a range of classes and does something non-standard with them. As an example, think of a persistence framework. You need to map classes to and from tables, and so you need to introspect the classes to understand what fields and methods they have. This scenario also comes up in application code, especially if you need to access an otherwise inaccessible part of a legacy library. Deciding which technology to use requires understanding what is needed. If you can resolve the issue with normal Java calls, that’s the way to go. If you need something more sophisticated, first look to the standard APIs, like MethodHandles and VarHandles. Only if these all fail to deliver should you fall back on reflection. Examples should help clarify why the JDK prefers handles to traditional Java reflection. Using reflection to access a method We’ll start with an example of reflection because it is familiar and will give us a known reference. Just remember that it is the solution of last resort. Say you have this class: public class MyClass { private String name; public MyClass(String name) { this.name = name; } public String getName() { return name; } } This is a very simple affair: just a class to hold string name and a getter and setter. To create this class, we can use normal instantiation: MyClass objectInstance = new MyClass('John Doe'); Here’s how to access the method using reflection: Class clazz = objectInstance.getClass(); Method method = clazz3.getDeclaredMethod('getName'); String value = (String) method.invoke(objectInstance); System.out.println(value); // prints 'John Doe' Using MethodHandles to access a method Method handles give us the same kind of power as reflection, but with safer syntax: Class clazz = objectInstance.getClass(); MethodHandle handle = MethodHandles.lookup().findVirtual(clazz, 'getName', methodType(String.class)); String value = (String) handle.invoke(objectInstance); System.out.println(value); // Prints “John Doe” We start off the same way, by obtaining the class from our instance. Then, we use the lookup().findVirtual() method on MethodHandles. This is one of the chief things that MethodHandles was designed to do: provide a cleaner, JDK-approved way to look up a method. This approach is also enhanced for JVM optimization. Next, we would call the method with the handle using handle.invoke and passing in the object instance. Java reflection with method handles The core reflection APIs were recentlyreimplemented using method handles under the hood. You can learn more about that adventure here. Directly accessing fields Now let’s say our earlier class, MyClass, has that name field on it but there is no accessor. We need something stronger to get at it now because we’re going to directly access the private member. Here’s how we’d do it using standard reflection: Class clazz = objectInstance.getClass(); Field field = clazz.getDeclaredField('name'); field.setAccessible(true); String value = (String) field.get(objectInstance); System.out.println(value); // prints “John Doe” Notice we are again directly working with the metadata of the object, like its class and the field on it. We can manipulate the accessibility of the field with setAccessible (this is considered risky because it might alter the restrictions that were put on the target code as written). This is the essential part of making that private field visible to us. Now let’s do the same thing using variable handles: Class
https://www.infoworld.com/article/3489879/better-than-reflection-using-method-handles-and-variable-h...
Voir aussi |
56 sources (32 en français)
Date Actuelle
sam. 23 nov. - 23:13 CET
|