Annotations

Java Annotations vs Python Decorators

  • They both share the same syntax
  • A decorator takes a function and return another function
  • Java annotations are just metadata and read by annotation processors
  • Java annotations are not decorators and hence not equivalent to python decorators

@Override

  • To override the function while extending/implementing another class
  • This is an optional annotation but it is recommended to use it for two benefits:
    • Compiler will check if the method is really overriding and throw error if it is not
    • The developer instantly knows that the method is overriding

@Deprecated

  • marked element is deprecated and should no longer be used
  • should also be documented using the Javadoc @deprecated tag
    /**
     * @deprecated
     * explanation of why it was deprecated
     */
    @Deprecated
    static void deprecatedMethod() { }

@SuppressWarnings

  • tells the compiler to suppress specific warnings that it would otherwise generate
  • Two most common warnings are deprecation and unchecked.:
    • deprecation: when using deprecated method or type
    • unchecked: when using raw types
  • @SuppressWarnings("deprecation")
  • https://www.baeldung.com/java-suppresswarnings

@FunctionalInterface

  • Indicate that the interface must have a single abstract method (SAM) inside
  • It must be used with interface
  • If any condition not fulfilled then compilation will fail as per Java Language spec
  • See functional_interface

@SafeVarargs

  • applied to a method or constructor
  • asserts that the code does not perform potentially unsafe operations on its varargs parameter.
// No warning thrown
private void printStringSafeVarargs(String... testStrings) {
}
 
// Throws warning since varargs used on generics
@SafeVarargs //  suppress warnings
private void printStringSafeVarargs(List<String>... testStringLists) {
}

Custom Annotations

  • Need to be defined in @interface files
  • Meta-Annotation:
    • Annotations that apply to annotations are called meta-annotation
  • @Retention: Retention Policies, how long annotation will be retained
    • RetentionPolicy.SOURCE
      • used by the compiler during the compilation process
      • example: @Override
    • RetentionPolicy.CLASS
      • default retention
      • recorded in class file generation
      • not available in runtime
      • used on code obfuscation or code generation (Lombok)
      • example: @NotNull
    • RetentionPolicy.RUNTIME
      • recorded in the class file
      • available to read at runtime
      • used for custom annotation
      • example: @Deprecated
  • @Target: Target types
    • ElementType.ANNOTATION_TYPE can be applied to an annotation type.
    • ElementType.CONSTRUCTOR — constructor.
    • ElementType.FIELD — field or property.
    • ElementType.LOCAL_VARIABLE — local variable.
    • ElementType.METHOD — method-level annotation.
    • ElementType.PACKAGE — package declaration.
    • ElementType.PARAMETER — parameters of a method.
    • ElementType.TYPE— any element of a class.
  • @Documented - takes no parameters and includes an annotation in the Javadoc, considered part of the element’s public contract
  • @Inherited - will be applied to all subclasses of the annotated class
  • @Repeatable - can be used multiple times at the same place
@Retention(RetentionPolicy.RUNTIME) // since it is custom annotation
@Target(ElementType.METHOD) // works on methods
public @interface Description {
 
}

@Repeatable

  • Provide a container annotation as argument
  • Define container annotation with value() element with an array type
  • For compatibility reasons, repeating annotations are stored in a container annotation that is automatically generated by the Java compiler
  • In order for the compiler to do this, two declarations are required in your code:
    • @Repeatable annotation
    • Container annotation type
  • https://docs.oracle.com/javase/tutorial/java/annotations/repeating.html
@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}
 
// Container annotation
public @interface Schedules {
    Schedule[] value();
}
 
// using the annotation
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

Annotation Parameters

  • They can only be of the following types:
    • Primitives
    • String
    • Class
    • Enum
    • annotation
    • an array of these types
  • default value cannot be null
  • If you don’t want to use the argument name, then name method as value()
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Description {
    String author();
    String description();
    int version() default 0;
}

Annotation Preprocessor

  • https://www.baeldung.com/java-annotation-processing-builder
  • Used by JPA, QueryDSL, Lombok etc.
  • You can only generate new files but cannot modify the existing ones
    • Lombok is an exception which uses some hacks to achieve its functionality
  • We create separate java module for Processor
    • The main logic class extends AbstractProcessor
  • The processor can be invoked using
    • CLI
      • javac -processor <processor-class> <main-class>
    • maven
      • maven-compiler-plugin
    • gradle
      • annotationProcessor