Loading...
Abstract:
Should we write "public abstract static class" or "public static abstract class"? And should fields be "final static private" or "private static final"? In this newsletter we explore the canonical order of modifiers in Java.
Welcome to the 293rd edition of The Java(tm) Specialists' Newsletter. Most mornings after the school drop, I get to grab a coffee at what must be one of the most efficient coffee bars anywhere. Voula spots me parking my Suzuki Jimny outside, and immediately gets to work grinding my arabica coffee beans. By the time my mask is on and I am waddling into her shop, she has already poured my "diplo espresso, sketo, choris kapaki" (double espresso, black without sugar, without the plastic lid). Yes, I have occasionally spilled my coffee, but it irks me to stick a plastic lid on something I will consume in the next few minutes anyway. The coffee is already rung up on her POS and after a few friendly words, I'm on my way. Most mornings go off without a hitch, but unfortunately Voula occasionally gets days off. On those days we have a hearty exchange trying to explain that yes, I do want a hot coffee, not cold, and no, I do not want sugar, I'm sure of that, and no, I also don't want the plastic lid but thank you so much for asking.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
I was reading some Java code the other day and began to notice that the order of the modifiers looked peculiar. Some fields were defined as final private then others as final static private and then others as the more typical private static final. I am so used to reading one particular sequence of modifiers, that this looked strangely messy. Perhaps my German Ordnung genes were kicking in? Or was there a canonical order that was preferred and which I have used for so long that I only notice it when it is not followed?
After digging around in IntelliJ IDEA's code analyzer, I found this inspection: Java -> Code style issues -> Missorted modifiers. It claimed that it would report declarations whose modifiers were not in the canonical preferred order (as stated in the Java Language Specification). Thus there was some order that was apparently preferred, but what was that order?
After searching in the Java Language Specification for a while, I found JLS section 8.3.1, which states that "If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier. Aha, so what are all the possible FieldModifiers?
FieldModifier: (one of) Annotation public protected private static final transient volatileThus the annotation should come first, and then the other modifiers in that order. Our code will compile even if we do not follow this canonical order, but it's a bit like leaving the toilet seat up in a shared house. As we will see, the OpenJDK follows this canonical order quite consistently.
Methods have a similar preferred order, defined in JLS section 8.4.3:
MethodModifier: (one of) Annotation public protected private abstract static final synchronized native strictfpConstructor is a bit easier, because only in the case of an annotation can we have more than one modifier, defined in JLS section 8.8.3:
ConstructorModifier: (one of) Annotation public protected privateThe class also has quite a few modifiers, especially since sealed classes were added. The most common mistake is to write static before abstract. Again, we can find the recommended order in JLS section 8.1.1:
ClassModifier: (one of) Annotation public protected private abstract static final sealed non-sealed strictfpIt is not too difficult to remember most of them. Annotations always come first. Then we have public/protected/private. Next up is abstract, if applicable. Then static and final. The rest is too rare to bother memorizing.
I sent IntelliJ IDEA off to look through the OpenJDK code. It found only 135 variations of this canonical modifier order. That is surprisingly few, considering that the OpenJDK has over 10 millions lines of Java code. The most common deviation was when annotations were not the first modifier, which happened 96/135 times, over 71%. Next up were 18 "static abstract" instead of the more canonical "abstract static". Then 12 public/protected/private after other modifiers. The 9 remaining places had mixed up final / static / native / transient and volatile.
After the OpenJDK, I decided to run the analyzer over the sample code for my Dynamic Proxies in Java book. I found 10 missorted modifiers, always "final static" instead of "static final". Oops. I also ran it over Jetty 10.x and in their 622k LOC did not find any missorted modifiers. Well done Jetty!
I then inspected Spring Framework with its 1.2m LOC. There we found 159 warnings, of which 79 were writing static abstract instead of abstract static, 46 were the annotations not the first modifier, 28 was final static instead of static final, the mistake that I made too and then only 6 had public/protected/final not immediately after the annotation.
It seems that the module-info.java file also follows a canonical order. I found only one module-info.java file in the OpenJDK that deviated from that slightly. Here is the order that I believe we should use. Always start with requires, so that anyone looking at our module-info.java file immediately sees what modules we depend on. Next we list all requires transitive, followed by requires static. We then show the packages that our module exports generally followed by those exports that are to a specific module. Next we list all the packages that we allow deep reflection on via opens, preferably to a specific module. Lastly we have uses and then provides for the ServiceLoader. The order can be seen in JLS Module Directive:
[open] module some.module.name { requires ... requires transitive ... requires static ... exports ... exports ... to ... ... ... opens ... opens ... to ... ... ... uses ... provides ... with ... ... ... }Also, the class names in uses and provides should be fully qualified, thus import statements should not be used.
Kind regards
Heinz
Java Specialists Superpack 21 Our entire Java Specialists Training in One Huge BundleLoading...
Loading...