I have developed a framework and corresponding API which includes a runtime-visible annotation. The API also supplies some helper methods intended for client use on objects whose classes have that annotation. Understandably, the helpers are tightly-coupled with the annotation, but it is important that their internals be encapsulated from the client. The helper methods are currently provided via static inner class within the annotation type...
@Target(TYPE)
@Retention(RUNTIME)
public @interface MyAnnotation {
// ... annotation elements, e.g. `int xyz();` ...
public static final class Introspection {
public static Foo helper(Object mightHaveMyAnnotation) {
/* ... uses MyAnnotation.xyz() if annotation is present ... */
}
}
}
... but the helpers could just as easily exist in some other top-level utility class. Either way provides the necessary amount of encapsulation from client code, but both incur extra costs to maintain a completely separate type, prevent them from instantiation since all useful methods are static, etc.
When Java 8 introduced static methods on Java interface types (see JLS 9.4), the feature was touted as providing the ability to...
... organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class.
— from Java Tutorials Interface Default Methods
This has been used within the JDK libraries to provide implementations such as List.of(...)
, Set.of(...)
, etc., whereas previously such methods were relegated to a separate utility class such as java.util.Collections
. By locating the utility methods within their related interfaces, it improves their discoverability and removes arguably unnecessary helper class types from the API domain.
Since I the current JVM bytecode representation for annotation types is very closely related to normal interfaces, I wondered if the annotations would also support static methods. When I moved the helpers into the annotation type, such as:
@Target(TYPE)
@Retention(RUNTIME)
public @interface MyAnnotation {
// ... annotation elements ...
public static Foo helper(Object mightHaveMyAnnotation) { /* ... */ }
}
... I was slightly surprised that javac complained with the following compile-time errors:
OpenJDK Runtime Environment 18.3 (build 10+46)
- modifier static not allowed here
- elements in annotation type declarations cannot declare formal parameters
- interface abstract methods cannot have body
Clearly, the Java language does not currently allow this. It may be that there are good design reasons to disallow it or, as previously presumed for static interface methods, "there [was] no compelling reason to do so; consistency isn't sufficiently compelling to change the status quo".
It is specifically not the goal of this question to ask "why doesn't it work?" or "should the language support it?", as to avoid opinion-based answers.
The JVM is a powerful technology and in many ways more flexible than what's allowed by the Java Language. At the same time, the Java Language continues to evolve, and today's answer may be obsolete tomorrow. With the understanding that such power must be used with great care...