Is extending an annotation interface meaningless and discouraged? - java

Here's an example of it in the JavaDoc of AnnotationLiteral e.g.
"An instance of an annotation type may be obtained by subclassing AnnotationLiteral."
public abstract class PayByQualifier extends AnnotationLiteral<PayBy> implements PayBy {
}
PayBy paybyCheque = new PayByQualifier() {
public PaymentMethod value() {
return CHEQUE;
}
};
There is a more complete example in Section 5.6.3 in the CDI spec.
5.6.3. Using AnnotationLiteral and TypeLiteral
javax.enterprise.util.AnnotationLiteral makes it easier to specify qualifiers when calling select():
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface Synchronous {}
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface PayBy {
PaymentMethod value();
#Nonbinding String comment() default "";
}
public PaymentProcessor getSynchronousPaymentProcessor(PaymentMethod paymentMethod) {
class SynchronousQualifier extends AnnotationLiteral<Synchronous>
implements Synchronous {}
class PayByQualifier extends AnnotationLiteral<PayBy>
implements PayBy {
public PaymentMethod value() { return paymentMethod; }
}
return anyPaymentProcessor.select(new SynchronousQualifier(), new PayByQualifier()).get();
}
And finally according to section 9.6 of the Java annotation types spec.:
Unless explicitly modified herein, all of the rules that apply to normal interface declarations apply to annotation type declarations.
For example, annotation types share the same namespace as normal class and interface types; and annotation type declarations are legal wherever interface declarations are legal, and have the same scope and accessibility.
However, the Java compiler complains when I attempt to implement an annotation. In particular, Intellij warns:
"Reports any classes declared as implementing or extending an annotation interface. While it is legal to extend an annotation interfaces, IT IS NEARLY MEANINGLESS, AND DISCOURAGED." (emphasis mine).
Here is the error message as displayed from within Intellij:
The Intellij warning seems to contradict the official Java documentation. I presume the Intellij warning is based upon a warning that comes from the Java compiler. What is correct? The Intellij and/or compiler warning or the documentation?

Annotations types are used as meta data. The typical use case for annotations, at runtime, is with reflection. You annotate something, then you use reflection to retrieve the annotation, process it, and possibly enhance the target. The annotation instances are created and given to you by the JVM through calls to the reflection API.
In that regard, creating your own annotation type instances, which AnnotationLiteral makes easier to do, is kind of pointless since you have no target, since nothing was actually annotated.
It can be useful for cases where you need to mock an annotation type instance or you want to inject some functionality that only exists when processing annotations.
Intellij is simply warning you that it's uncommon.

From Intellij support:
"We can change warning text for example to: "While it is legal to extend an annotation interface it is often done by accident, and the result won't be usable as an annotation." Note that you can always suppress the warning for the statement."
My response:
The proposed solution... would require developers to always suppress the warning for the statement. This solution is poor because the code would be littered with #SuppressWarning or, worse, the developer would need to turn off the warning altogether. The best and most useful solution, would be to not display any warning whatsoever if the developer extends AnnotationLiteral or TypeLiteral and implements an #interface at the same time.
A YouTrack Issue was filed.

Related

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"?
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());
}
}
}
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.

`ElementType.FIELD` vs `ElementType.TYPE_USE`

I not fully understand the difference between annotating variable and annotating its type.
Should I prefer type annotation over declaration annotation in this scenario?
#EmailField // can be used on String or Array/Collection of String-s
private String email;
private #EmailType String email2;
#EmailField
private List<#EmailType String> emails;
#Target(ElementType.TYPE_USE)
#interface EmailType {}
#Target(ElementType.FIELD)
#interface EmailField {}
Type annotations in Java 8 are mainly around to support stronger type checking.
From https://docs.oracle.com/javase/tutorial/java/annotations/type_annotations.html:
Type annotations were created to support improved analysis of Java
programs way of ensuring stronger type checking. The Java SE 8 release
does not provide a type checking framework, but it allows you to write
(or download) a type checking framework that is implemented as one or
more pluggable modules that are used in conjunction with the Java
compiler.
From your above example, you should use the #EmailField field annotation, and you should also add #Retention(RetentionPolicy.RUNTIME) so that you can use reflection to check for this annotation at runtime as follows:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface EmailField {}
Here is the difference between declaration annotations and type annotations:
A declaration annotation on a field gives information about the variable, such as that the field is deprecated and clients should use a getter method instead.
A type annotation gives information about the value, such as that an integer value is greater than zero.
Your annotation gives information about the values: the string should be a legal email address. Therefore, you should use a type annotation, #EmailType (though I would just name it #Email).
You can validate a type annotation at compile time, run time, or both.
Simple example is still much easier to understand than many explanations.
TYPE_USE is use for this purpose :
private List<#ValidEmail String> emails;

Order of automatically wrapped #Repeatable annotations

Before, I used to declare a wrapper annotation by hand, with an array, and then call it like so:
#Foos({ #Foo(0), #Foo(1), #Foo(2) })
public void bar() {}
Since I was making an array with the { ... } initializers, it was more than clear that the order was to be the same of the declaration when I accessed this method later via Reflection.
However, when I use the new #Repeatable annotation from Java 8, do I have guarantee that the order will be kept?
I declared a simple set of annotations:
public #interface Foos {
Foo[] value();
}
#Repeatable(Foos.class)
public #interface Foo {
int value();
}
and run some tests with the most diverse scenarios:
#Foo(0) #Foo(1) #Foo(2)
public void bar1() {}
#Foo(2)
#Deprecated #Foo(5)
#Foo(10)
public void bar2() {}
and everything seems to work flawlessly (when accessing via Reflection), but I would not like to rely on undocumented stuff. It seems obvious to me that that should be the case, but I can't find it anywhere! Can anyone shed a light on this?
Here's what the Java Language Specification says:
If a declaration context or type context has multiple annotations of a repeatable annotation type T, then it is as if the context has no explicitly declared annotations of type T and one implicitly declared annotation of the containing annotation type of T.
The implicitly declared annotation is called the container annotation, and the multiple annotations of type T which appeared in the context are called the base annotations. The elements of the (array-typed) value element of the container annotation are all the base annotations in the left-to-right order in which they appeared in the context.
(emphasis mine)
So yes, the order of the annotations in the container annotation is the same as the order of declaration of the repeatable annotations.

Annotation applicable to specific data type

I don't know if the question I am asking is really stupid. But here it is:
I would like to write a custom annotation which should be applicable to a specific type. For example, if I have a class A, then I would like to have an annotation that can be applied on objects of A.
Something like this:
#Target({ElementType.FIELD, //WHAT_ELSE_HERE_?})
public #interface MyAnnotation {
String attribute1();
}
public class X {
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
String str;
#MyAnnotation (attribute1="..") //PERFECTLY VALID
A aObj1;
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
B bObj1;
}
Is that possible at all?
Not possible. #Target uses ElementType[], and ElementType is an enum, so you can't modify it. It does not contain a consideration for only specific field types.
You can, however, discard the annotation at runtime, or raise runtime exceptions about it.
That is not possible in Java.
But you have an option to write your own annotation processor if you want to check the correctness of the annotations before runtime.
Annotation processing is a hook in the compile process, to analyse the
source code for user defined annotations and handle then (by producing
compiler errors, compiler warning, emmiting source code, byte code
..).
A basic tutorial on Annotation Processing.

Can a Java class which implements an interface inherit the annotations automatically?

Suppose I had an interface with some annotation(s), for example:
#SpecialClass
public interface IFoo { /* ... */ }
And suppose I make a class that implements the interface:
public class Foo implements IFoo { /* ... */ }
Is it possible for class Foo to somehow "inherit" or automatically copy all or some of the annotations from IFoo and its members (e.g. automagically annotate Foo as #SpecialClass, etc.)?
This would be convenient for implementing web service classes (e.g. those generated by the JAX-WS "wsimport" tool) by just implementing their annotated interfaces without explicitly having to copy the interface annotations to the implementing class (e.g. javax.jws.WebService, javax.xml.ws.RequestWrapper, etc).
EDIT: I'm leaving this answer here for general information and future readers, but Andreas pointed out an important bit of the Javadoc which I'd missed:
Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.
In other words, it wouldn't help in this situation. Also it's only useful if you have control over the annotation itself, of course.
I suspect the real answer is that you simply have to apply the annotation everywhere. If you're worried about forgetting one, you might want to write a unit test which finds all your classes (easier said than done, I realise) and checks that the annotation is present for all classes implementing the given interface.
Have you tried applying the Inherited annotation to the SpecialClass annotation itself?
Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.
That certainly sounds like exactly what you want.

Categories