Session 20, 21|Java Annonations¶
- Java Annotation Fundamentals by Mr. Nataraj
- Overview of Java Built-in Annotations
- Creating a Custom Annotation in Java
- Introduction to Javadoc
What are Annotations in Java?¶
- Annotations are a way to add metadata to Java code.
- Their usage is optional.
- This metadata can be accessed at runtime, allowing you to add custom logic based on the information provided by the annotation.
- Annotations can be applied to various elements in the code, such as classes, methods, interfaces, fields, parameters, etc.
In [1]:
public interface Bird {
public void hi();
}
public class Eagle implements Bird {
@Override
public void hi() {
System.out.println("Hello, World");
}
}
In [2]:
Eagle eagle = new Eagle();
eagle.hi();
Hello, World
Types of Annotations¶
- Predefined Annotations
- Used on Java Code (e.g., classes, methods):
@Deprecated: Marks a class, method, or field as deprecated, signaling that it should no longer be used and may be removed in the future.@Override: Indicates that a method overrides a method from a superclass.@FunctionalInterface: Marks an interface as a functional interface (an interface with a single abstract method).@SafeVarargs: Suppresses warnings for heap pollution in varargs usage.@SuppressWarnings: Suppresses specific compiler warnings.
- Used on Top of Another Annotations (Meta-Annotations):
@Target: Specifies where the annotation can be applied (e.g., methods, fields, classes).@Retention: Defines how long annotations are retained (e.g., runtime, compile-time, or source-only).@Documented: Indicates that the annotation should be included in the JavaDocs.@Inherited: Allows subclasses to inherit the annotation from a superclass.@Repeatable: Allows an annotation to be applied multiple times to the same element.
- Used on Java Code (e.g., classes, methods):
- Custom Annotations
- Developers can create their own annotations tailored to specific program requirements.
@Deprecated¶
- Indicates that a class, method, field, or other elements are deprecated.
- Generates a compile-time warning when the deprecated element is used.
- Suggests using a new alternative as no further improvements will be made to the deprecated element.
- It can be applied to a Constructor, Field, Local Variable, Method, Package, Parameter, or Type (class, interface, enum).
In [3]:
public class DeprecatedDemo {
@Deprecated(since = "4.5", forRemoval = true)
public void testLegacyFunction() {
System.out.println("This is a legacy function");
}
}
DeprecatedDemo dep = new DeprecatedDemo();
dep.testLegacyFunction();
This is a legacy function
In [4]:
public class Mobile {
@Deprecated
public void dummyMethod() {}
}
Mobile mobile = new Mobile();
mobile.dummyMethod();
@Override¶
- During compile time, it checks whether the method is correctly overriding a method from the parent class.
- It throws a compile-time error if the method does not match the parent method's signature.
- It can only be used with methods.
In [5]:
public class Employee {
public void getEmployeeStatus(){
System.out.println("This is the Base Employee class");
}
}
public class Manager extends Employee {
@Override
public void getEmployeeStatus(){
System.out.println("This is the Manager class");
}
}
@FunctionalInterface¶
- Restricts the interface to have only one abstract method.
- Throws a compilation error if more than one abstract method is found.
- It can be used on Interfaces.
In [6]:
@FunctionalInterface
public interface Bird {
public void fly();
}
In [7]:
@FunctionalInterface
public interface Bird {
public void fly();
public void eat();
}
| @FunctionalInterface Unexpected @FunctionalInterface annotation Bird is not a functional interface multiple non-overriding abstract methods found in interface Bird
@SuppressWarnings¶
- Instructs the compiler to ignore any compile-time warnings.
- Use with caution, as ignoring valid warnings could lead to runtime exceptions.
- It can be used with Field, Method, Parameter, Constructor, Local Variable, and Type (Class, Interface, or Enum).
In [8]:
public class Mobile {
@Deprecated
public void dummyMethod() {}
@SuppressWarnings("unused")
public void unusedMethod() {
// if there is any unused merhod compile will throw warning
// to remove the warning you can use @SuppressWarnings("unused")
}
}
In [9]:
// class level
@SuppressWarnings("deprecation")
public class Abc {
public static void main(String... args) {
Mobile mobile = new Mobile();
mobile.dummyMethod();
}
}
In [10]:
// to ignore all the warnings use @SupressWarnings("all")
// method level
public class Abc {
@SuppressWarnings("deprecation")
public static void main(String... args) {
Mobile mobile = new Mobile();
mobile.dummyMethod();
}
}
@SafeVarargs¶
- Used to suppress heap pollution warnings.
- Can be applied to methods and constructors that have variable arguments
...varas parameters. - The method must be either
staticorfinal. - Since Java 9, it can also be used on private methods.
In [11]:
public interface abc {
// you can do this
void printStrings(String... stringList);
// instead of having to do this
void printStrings2(String string1, String string2);
}
In [12]:
public class Log {
public static void printLog(List<Integer>... logNumbersList) {
Object[] objectList = logNumbersList;
List<String> stringValues = new ArrayList<>();
stringValues.add("Hello");
objectList[0] = stringValues;
}
}
In [13]:
public class Log {
@SafeVarargs // gives heap pollution warning
public static void printLog(List<Integer>... logNumbersList) {
Object[] objectList = logNumbersList;
List<String> stringValues = new ArrayList<>();
stringValues.add("Hello");
objectList[0] = stringValues;
}
}
In [14]:
import java.util.Arrays;
import java.util.List;
public class SafeVarargsTest {
void printString(String test1, String test2) {
System.out.println(test1);
System.out.println(test2);
}
void printStringVarargs(String... tests) {
for (String test : tests) {
System.out.println(test);
}
}
void printStringSafeVarargs(List<String>... testStringLists) {
for (List<String> testStringList : testStringLists) {
for (String testString : testStringList) {
System.out.println(testString);
}
}
}
}
SafeVarargsTest test = new SafeVarargsTest();
test.printString("String1", "String2");
System.out.println("-------");
test.printStringVarargs("String1", "String2");
System.out.println("-------");
List<String> testStringList1 = Arrays.asList("One", "Two");
List<String> testStringList2 = Arrays.asList("Three", "Four");
test.printStringSafeVarargs(testStringList1, testStringList2);
String1 String2 ------- String1 String2 ------- One Two Three Four
Annotation Usage Over Other Annotations¶
Element Types¶
Annotations in Java can be applied to various element types, as listed below:
- TYPE: Used to annotate classes, interfaces, or enums.
- FIELD: Applied to instance variables or fields.
- METHOD: Used for methods.
- PARAMETER: Applied to method parameters.
- CONSTRUCTOR: Used to annotate constructors.
- LOCAL_VARIABLE: Applied to local variables within a method or block.
- ANNOTATION_TYPE: Used to annotate other annotations.
- PACKAGE: Applied to package declarations.
- TYPE_PARAMETER: Used for generic type parameters.
- TYPE_USE: Introduced in Java 8, this allows annotations to be used anywhere a type is declared (e.g., List<@Annotation String>).
@Target¶
The @Target meta-annotation is used to specify where an annotation can be applied.
For example, you can restrict an annotation's usage to methods, constructors, fields, etc., based on the defined target types.
In [15]:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
public @interface Override {}
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}
@Retention¶
The @Retention meta-annotation specifies how an annotation is stored in Java. It determines the lifecycle of the annotation and where it is accessible.
Retention Policies:¶
- RetentionPolicy.SOURCE: The annotation is discarded by the compiler and is not included in the
.classfile. - RetentionPolicy.CLASS: The annotation is recorded in the
.classfile but ignored by the JVM at runtime. - RetentionPolicy.RUNTIME: The annotation is recorded in the
.classfile and is available at runtime.
Example 1¶
In [16]:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}
public interface Bird {
public void fly();
}
public class Eagle implements Bird {
@Override
public void fly() {
return;
}
}
In [17]:
// .class file (@Override is missing)
public class Eagle implements Bird {
public Eagle() {}
public void fly() {
return;
}
}
Example 2¶
In [18]:
import java.util.List;
import java.util.ArrayList;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface SafeVarargs {}
public class Log {
@SafeVarargs
public static void printLogValues(List<Integer>...logNumbersList) {
Object[] objectList = logNumbersList;
List<String> stringValuesList = new ArrayList<>();
stringValuesList.add("hello");
objectList[0] = stringValuesList;
}
}
In [19]:
// .class file
public class Log {
public Log() {}
@SafeVarargs
public static void printLogValues() {}
}
Example 3¶
In [20]:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation {}
@MyCustomAnnotation
public class Test {}
System.out.println(new Test().getClass().getAnnotation(MyCustomAnnotation.class));
@REPL.$JShell$42.MyCustomAnnotation()
Example 4¶
In [21]:
// without @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyCustomAnnotation {}
@MyCustomAnnotation
public class Test {}
System.out.println(new Test().getClass().getAnnotation(MyCustomAnnotation.class));
null
@Documented¶
- By default, annotations are not included in the generated Java documentation.
- Using the
@Documentedmeta-annotation ensures that the annotated annotation is included in the Java documentation (Javadoc). - To include an annotation in the documentation, apply
@Documentedat the top of the annotation definition.
In [22]:
@Documented
@interface MyAnnotation {
String value();
}
@Inherited¶
- By default, annotations applied to a parent class are not inherited by its child classes.
- However, with the use of the
@Inheritedmeta-annotation, annotations on the parent class become available to child classes.
- However, with the use of the
- This meta-annotation only works when the annotation is applied to a class. It has no effect if the annotation is applied to other elements like methods or fields.
In [23]:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyCustomAnnotation {}
@MyCustomAnnotation
public class Parent {}
public class Child extends Parent {}
System.out.println(new Child().getClass().getAnnotation(MyCustomAnnotation.class));
@REPL.$JShell$42E.MyCustomAnnotation()
In [24]:
// @Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyCustomAnnotation {}
@MyCustomAnnotation
public class Parent {}
public class Child extends Parent {}
System.out.println(new Child().getClass().getAnnotation(MyCustomAnnotation.class));
null
@Repeatable¶
- Allows the use of the same annotation more than once at the same location.
In [25]:
@Deprecated
@Deprecated
public class ABC {}
| @Deprecated java.lang.Deprecated is not a repeatable annotation interface | @Deprecated | @Deprecated | public class ABC {} deprecated item is not annotated with @Deprecated
In [26]:
// before Java 8, we couldn't do this
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Category {
String name();
}
In [27]:
@Category(name = "Bird")
@Category(name = "LivingThings")
public class Eagle {
public void fly() {}
}
| @Category(name = "LivingThings") Category is not a repeatable annotation interface
In [28]:
// you need to use @Repetable meta annotation to do this
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Categories {
Category[] value();
}
@Repeatable(Categories.class)
public @interface Category {
String name();
}
In [29]:
@Category(name = "Bird")
@Category(name = "LivingThings")
@Category(name = "Carnivorous")
public class Eagle {
public void fly() {}
}
Category[] categories = new Eagle().getClass().getAnnotationsByType(Category.class);
for (Category annotation: categories) {
System.out.println(annotation.name());
}
Bird LivingThings Carnivorous
User-Defined or Custom Annotations¶
We can create custom annotations using the @interface keyword.
In [30]:
// annotation with empty body
public @interface CustomAnnotation {}
@CustomAnnotation
public class Eagle {
public void fly() {}
}
In [31]:
// annotation with method (like a variable)
public @interface CustomAnnotation {
// No parameter, no body
// Return type is restricted to Primtitive, Class, String, Enum, annotations and array of these types.
String name();
}
@CustomAnnotation(name="Max")
public class Eagle {
public void fly() {}
}
In [32]:
// annotation with default value
public @interface CustomAnnotation {
// default value can't be null
String name() default "John";
}
@CustomAnnotation()
public class Eagle {
public void fly() {}
}