Laden...
Abstract:
Reflection returns the modifiers of class elements as an unqualified int bitset. Unfortunately some of the bits have a different meaning depending on their context. For example, a method can have their transient bit set, even though that does not make sense for a method. In Java 20, we now have a more accurate and robust enum based representation for modifiers.
Welcome to the 307th edition of The Java(tm) Specialists' Newsletter. I spent most of March visiting my mom in Bantry Bay, the suburb of Cape Town where I grew up. The last time we had seen each other face-to-face was in September 2019. My wife asked what I would be doing during my time in South Africa, besides hanging out with Mutti. My intention was to write 4 or 5 newsletters in the evenings, so that I would have them ready for the upcoming months. But our time together was far too precious, that I didn't even get a chance to think of new topics. Now I'm back home in Crete, scrambling to send this out before another month is over. Hope you enjoy it!
Java 9-11 have 120 JEPs, and Java 12-17 have 74. The JEPs define the larger new features of the language. Not all JEPs are interesting, but some certainly are. We have created two new courses on Java 11 and Java 17. Our courses take the most useful JEPs and explore them with lectures, demos and exercises. Please let me know if your team is looking to migrate to a newer version of Java and I'll be happy to help you. The Java 11 migration course takes 3 days and the Java 17 migration 2 days. We can do the courses either on-site or remote via Zoom or Teams. More information here: Java 11 Course and Java 17 Course, or just pop me an email.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
When Java added reflection in Java 1.1, it revolutionized the language. Without knowing the type at compile time, we could create objects, call methods, and access fields. It empowered a whole plethora of frameworks to make Java as successful as it is today. Reflection is exceedingly useful, when used correctly.
However, one of the things that has always frustrated me was how hard it was to parse the modifiers of members. For example, we could not say method.isStatic(). Oh no, instead we had to read the modifiers bitset and then parse that with the Modifier class, like so: Modifier.isStatic(method.getModifiers()).
That not being bad enough, all unqualified bitsets have the tendency to be abused over time. For example, when biased locking was introduced into Java, they commandeered some of the bits from the object headers that were used for the identity hash code. If we somehow generated the identity hash code, for example by calling the default toString() method, then biased locking would no longer work. Similarly, some of the Modifier bits were expropriated to represent new features in Java. For example, a method that has vararg parameters is marked as transient, a keyword that would only make sense with fields, and even then barely so. Another strange one are bridge methods, which are marked as volatile. Thus the modifier bits only make sense when we consider their context.
Here is an example class with three declared methods. foo() combines as many modifiers as possible, varArgsMethod() shows what happens when we use var-args as our last parameter, and compareTo() demonstrates bridge and synthetic methods.
public class MethodsDemo implements Comparable<MethodsDemo> { private static final synchronized strictfp void foo() {} void varArgsMethod(Object... args) {} // bridge (volatile) & synthetic public int compareTo(MethodsDemo o1) { return 0; } }We can investigate the methods with this AccessFlagDemo:
import java.lang.reflect.*; import java.util.*; import java.util.stream.*; public class AccessFlagDemo { public static void main(String... args) { Arrays.stream(MethodsDemo.class.getDeclaredMethods()) .sorted(Comparator.comparing(Method::getName)) .forEach(method -> System.out.println(""" %s: Method: %s Modifiers: %s Modifiers Hex: %s AccessFlags: %s """.formatted(method.getName(), method, Modifier.toString(method.getModifiers()), hexValues(method.getModifiers()), method.accessFlags()))); } private static String hexValues(int modifiers) { int bit = 1; List<Integer> values = new ArrayList<>(); while (modifiers != 0) { if ((modifiers & bit) != 0) values.add(bit); modifiers = modifiers & ~bit; bit <<= 1; } return values.stream() .map(val -> String.format("0x%04x", val)) .collect(Collectors.joining(" ", "[", "]")); } }When we run the code, we see the following:
compareTo: Method: public int MethodsDemo.compareTo(MethodsDemo) Modifiers: public Modifiers Hex: [0x0001] AccessFlags: [PUBLIC] compareTo: Method: public int MethodsDemo.compareTo(java.lang.Object) Modifiers: public volatile Modifiers Hex: [0x0001 0x0040 0x1000] AccessFlags: [PUBLIC, BRIDGE, SYNTHETIC] foo: Method: private static final synchronized void MethodsDemo.foo() Modifiers: private static final synchronized Modifiers Hex: [0x0002 0x0008 0x0010 0x0020] AccessFlags: [PRIVATE, STATIC, FINAL, SYNCHRONIZED] varArgsMethod: Method: void MethodsDemo.varArgsMethod(java.lang.Object[]) Modifiers: transient Modifiers Hex: [0x0080] AccessFlags: [VARARGS]The AccessFlags have more accurate information than the plain bits, since they consider the context of where the bits come from. If we simply call Modifier.toString(modifiers), we will think that the varArgsMethod() is transient. The result of the accessFlags() method is an EnumSet, wrapped in an unmodifiable set.
Note that our method foo() is not shown as strictfp. This keyword is redundant since Java 17 and is no longer compiled into the bytecode. If we compile the MethodsDemo class with Java 16 or before, then foo() will have that modifier. See JEP 306 for more information.
Whilst the AccessFlags are a small addition to the reflection API, it is a welcome change for anyone who has battled with modifiers. Thank you!
King regards
Heinz
Java Specialists Superpack 22 Our entire Java Specialists Training in One Huge BundleLaden...
Laden...