Consider this declaration of an annotation type Foo
:
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({ ANNOTATION_TYPE, TYPE, TYPE_PARAMETER, TYPE_USE })
public @interface Foo {}
Here I think ANNOTATION_TYPE
is made redundant by TYPE
, but I wonder what is made redundant by TYPE_USE
. The API has this to say:
The constant
TYPE_USE
corresponds to the type contexts in JLS 4.11 , as well as to two declaration contexts: type declarations (including annotation type declarations) and type parameter declarations.For example, an annotation whose type is meta-annotated with …
TYPE_USE
may be written on the type of a field … and may also appear as a modifier for, say, a class declaration.
The JLS says much the same.
Does this mean that target TYPE_USE
makes each of the other targets of Foo
redundant? Or what would change if I dropped them from the declaration?
No. On the contrary - they almost (but not quite) are mutually exclusive with TYPE_USE.
TYPE_USE refers to the concept of annotating a, well, usage of a type someplace. Whereas TYPE refers to annotating a type.
Example of putting an annotation on a thing in the sense of TYPE:
@MyAnno class ThisDefinesAClass {}
Example of putting an annotation on a thing in the sense of TYPE_USE:
public void example(List<@ThisIsTypeUse String> names) {}
Note how the TYPE_USE annotation is associated with the USAGE of a type, not the definition of a type. We aren't defining String. Merely using it.
Where it gets a little nutty is in something like this:
public @ThisIsUhWhatIsThis String example() {}
This is ambigious: Are you annotating the definition of the example
method, or, are you annotating the usage of String
, as part of the return type of the example
method?
If your annotation is specced to allow TYPE_USE only, then this is considered as annotating the String
part. If the annotation is specced to allow METHOD, then this is considered to be annotating the entire method.
If it's specced to allow BOTH , then it's a weird mishmash of rules that requires reviewing the JLS every time because they are neccessarily weird and confusing. Thus, it is almost always a good idea NOT to mix TYPE_USE with anything else.
Sometimes the definitions boil down to the same thing, but usually TYPE_USE is the better way to think about it. Example:
public @NonNull String getFoo() {return foo;}
This annotation is ambiguous. However, both interpretations lead to the same conclusion:
This annotation means that the entire method 'getFoo' has been annotated as a 'NonNull' method. Whilst one can conjure up any number of things as to what this might mean, if I tell you that it means: "It will never return null", I'm sure you'd find that a completely plausible thing (and it is, in fact, what @NonNull annotations that predate the existence of TYPE_USE mean, and for presumably backwards compat reasons, a lot of nonnull annotations still work like that: They target methods and params and fields, not TYPE_USE).
The alternative interpretation says that this annotation means that the use of 'String' as return type is so annotated: This is a method. Its return type is @NonNull String
, as in, the type String
, but tagged as @NonNull
. This means, of course, the same thing: This method cannot return null. But this way of thinking makes more sense. For example, the old way means you simply cannot communicate the notion of 'the first parameter of this method must either be a null pointer, or a pointer to some instance of List, which is constrained to contain only Strings. Those pointers to Strings that the list has must ALL be non-null. That'd be @Nullable List<@NonNull String>
, and you need TYPE_USE to express concepts like this.
Yes, given a target of TYPE_USE
, nothing changes if you add TYPE
, ANNOTATION_TYPE
or TYPE_PARAMETER
to the list of targets. Targeting TYPE_USE
makes all other 'TYPE' targets redundant.
During the public review of the JSR that introduced the TYPE_USE
constant, co-lead Alex Buckley wrote :
The
Foo
type has aTYPE_USE
target, so@Foo
can appear in at least the places permitted by aTYPE
target, which includes declarations of annotation types.
And again , in the same thread:
JSR 308 introduced
ElementType.TYPE_USE
as pertaining not only to uses of types but also to declarations of types; logically, it's a "supertype" ofElementType.TYPE
.
Recall that ElementType
defines its constants each as a class of 'syntactic locations… where it is legal to write annotations'. Taking Alex at his word, then, TYPE_USE
is not specific to locations of use, but a catch-all for locations of declaration and use. Logically a TYPE
location is a TYPE_USE
location. The same logic applies to the other 'TYPE' constants, which means these two declarations of Foo
are equivalent:
@Target({ TYPE_USE, TYPE, ANNOTATION_TYPE, TYPE_PARAMETER })
public @interface Foo {} // is the same as:
@Target({ TYPE_USE })
public @interface Foo {}
It might help to think of TYPE_USE
as a superset of the other 'TYPE' locations. Alex might even have meant to say 'superset' instead of 'supertype'. Either way, the conclusion would be the same: TYPE_USE
alone covers all the other 'TYPE' locations.
It would certainly help if the constants could somehow be renamed. A catch-all by the name of 'TYPE' (instead of 'TYPE_USE') together with 'TYPE_DECLARATION' (instead of 'TYPE'), and so forth, would make things much clearer.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.