Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does JPMS allow annotation types as services

In introducing JPMS services, section 7.7.4 of the Java Language Specification notes that "The service type must be a class type, an interface type, or an annotation type."

I'm struggling to see the point of permitting an annotation. My understanding is that the JPMS notion of a service is something for which we expect to select an implementation at runtime. It also seems that, to be useful, the implementation needs at least the possibility of being something other than the original class that identifies the service being requested. But I believe an annotation cannot use "extends" so this could never happen? From that, I reach the belief that if I try to make a service out of an annotation type, I'd inevitably end up with a situation where the only thing that could ever be returned by a service lookup on, for example, SomeAnnotation.class would be exactly SomeAnnotation. That seems pointless, so I must assume I'm missing something.

Can anyone shed light on this, and perhaps offer examples of how an annotation might be a "service"?

like image 533
Toby Eggitt Avatar asked Jan 04 '20 18:01

Toby Eggitt


2 Answers

It seems that you have missed another addition to the service providers. Within a named module, a service provider may return the implementation from a static method:

  • If the service provider declares a provider method, then the service loader invokes that method to obtain an instance of the service provider. A provider method is a public static method named "provider" with no formal parameters and a return type that is assignable to the service's interface or class.

    In this case, the service provider itself need not be assignable to the service's interface or class.

from ServiceLoader

So the following would work:

module Example.Module {
    uses example.Anno;
    provides example.Anno with example.AnnoProvider;
}
package example;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Anno {
    int value();
}
package example;

@Anno(42)
public class AnnoProvider {
    public static Anno provider() {
        return AnnoProvider.class.getAnnotation(Anno.class);
    }
}
package example;

import java.util.ServiceLoader;

public class ServiceUser {
    public static void main(String[] args) {
        for(Anno a: ServiceLoader.load(Anno.class)) {
            System.out.println(a.value());
        }
    }
}
like image 51
Holger Avatar answered Oct 01 '22 12:10

Holger


While in Java an annotation interface cannot explicitly extend any interfaces (but implicitly it always extends java.lang.annotation.Annotation), it can be implemented. I.e. it is syntactically possible to write a concrete class implementing an annotation interface, though according to JLS 9.6. Annotation Types such a class does not represent an annotation type:

a subclass or subinterface of an annotation type is never itself an annotation type

Thus I believe that the original question boils down to "why would anyone want to explicitly implement an annotation interface?". This question has already been asked and answered on SO: Use cases for implementing annotations. The accepted answer there proposes to do this in order to partially overcome the limitation that a value of an annotation element must be either a constant expression, or a class literal, or an enum constant (see JLS 9.7.1. Normal Annotations): one may implement an annotation interface to "annotate" the implementing class with an "annotation" that includes dynamic data taken e.g. from a config file, a database, etc. Obviously, such a technique also requires small changes in the code that reads annotations, as the class implementing an annotation interface is not actually annotated, but instead its instance can be used as an instance of an annotation as if it was retrieved e.g. via java.lang.Class.getAnnotationsByType.

like image 36
Valentin Kovalenko Avatar answered Oct 01 '22 11:10

Valentin Kovalenko