Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of having annotation tmesis/inside Java qualified types?

Does this have a purpose static @NotNull @Other My.@NotNull @Other Builder createBuilder()

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

public class A {
    static class My {
        static class Builder {
            public My build() {
                return new My(); } } }

    @Target({ElementType.METHOD, ElementType.TYPE_USE})
    public @interface NotNull { }

    @Target({ElementType.METHOD, ElementType.TYPE_USE})
    public @interface Other { }

    public static @NotNull @Other My.@NotNull @Other Builder createBuilder() {
        return new My.Builder();
    }
}
like image 582
Peter Lawrey Avatar asked Jan 07 '21 15:01

Peter Lawrey


People also ask

Why do we use annotations in Java?

Annotations are used to provide supplemental information about a program. Annotations start with '@'. Annotations do not change the action of a compiled program. Annotations help to associate metadata (information) to the program elements i.e. instance variables, constructors, methods, classes, etc.

What is the function of @target annotation in Java?

Annotation Type Target. Indicates the contexts in which an annotation type is applicable. The declaration contexts and type contexts in which an annotation type may be applicable are specified in JLS 9.6. 4.1, and denoted in source code by enum constants of java.

What is @interface annotation in Java?

Annotation is defined like a ordinary Java interface, but with an '@' preceding the interface keyword (i.e., @interface ). You can declare methods inside an annotation definition (just like declaring abstract method inside an interface). These methods are called elements instead.

How are annotations executed in Java?

Annotations don't execute; they're notes or markers that are read by various tools. Some are read by your compiler, like @Override ; others are embedded in the class files and read by tools like Hibernate at runtime. But they don't do anything themselves.


1 Answers

The main reason to use such a construct would be backward compatibility.

Prior to Java 8, there were no type annotations, so method annotations were often used to actually described the return type of a method like

@Target(ElementType.METHOD)
public @interface NotNull { }

@Target(ElementType.METHOD)
public @interface Other { }

public static @NotNull @Other My.Builder createBuilder() {
    return new My.Builder();
}

Starting with Java 8, you can annotate the return type itself and that’s what you normally would do. But to support old tools still looking for method annotations, you can keep the @Target METHOD. For return types consisting of a simple name, the code location for method annotations and return type annotations is the same, so you can create a method annotation and a return type annotation at the same time with a single occurrence, i.e.

@MethodAndTypeAnnotation ReturnType method() …

However, for qualified names, the syntax is different, as type annotation must be placed immediately prior to the simple name of the annotated element, i.e.

@Target(ElementType.TYPE_USE)
public @interface NotNull { }

@Target(ElementType.TYPE_USE)
public @interface Other { }

public static My.@NotNull @Other Builder createBuilder() {
    return new My.Builder();
}

For a pure type annotation using public static @NotNull @Other My.Builder createBuilder() would cause a compiler error, as it would be an attempt to annotate My which only serves as a qualifier here, not an actual type use. Note that if Builder was an inner class rather than a nested class (i.e. not static), it would be legal to annotate the outer type, though unlikely to be desired.

So in your case

@Target({ElementType.METHOD, ElementType.TYPE_USE})
public @interface NotNull { }

@Target({ElementType.METHOD, ElementType.TYPE_USE})
public @interface Other { }

//            method annotation    type annotation
public static @NotNull @Other   My.@NotNull @Other Builder createBuilder() {
    return new My.Builder();
}

both occurrences are required to annotate the method and the return type. As said, if the Builder was an inner class, the first occurrence would be legal to annotate the outer type and it would annotate it whether you want or not.

So in general, it’s not recommended to mix the “type use” annotation scope with others but to update code processing tools still requiring method or field annotations for tasks that are actually a type annotation’s job.


As said, for simple names you can annotate the method and return type at once, which also works for nested types when you use an import statement. But this requires the top level class to be in a package:

package example;

import example.A.My.Builder;

public class A {
    static class My {
        static class Builder {
            public My build() {
                return new My(); } } }

    @Target({ElementType.METHOD, ElementType.TYPE_USE})
    public @interface NotNull { }

    @Target({ElementType.METHOD, ElementType.TYPE_USE})
    public @interface Other { }

    public static @NotNull @Other Builder createBuilder() {
        return new My.Builder();
    }
}
like image 79
Holger Avatar answered Sep 29 '22 08:09

Holger