last modified July 4, 2024
In this article we talk about Java annotations.
Annotations are a special kind of code element that provides additional information about a program. They are attached to classes, methods, fields, and other program elements using the @ symbol.
Annotations themselves don't directly affect how the code runs, but instead provide metadata that can be used by various tools:
The @Override annotation in Java is specifically used for method
overriding in inheritance. It's a marker annotation to improve code clarity and
catch errors during compilation.
@Override does:
@Override before a method
declaration in a subclass, you explicitly tell the compiler that you
intend to override a method from the superclass.
@Override annotation actually overrides a method with the
same name, parameter list, and return type in the direct superclass. If
not, it throws a compilation error. This helps prevent mistakes like
typos in method names or accidental overloads (methods with the same
name but different parameter lists).
@Override makes your code
clearer by highlighting methods that are intended to override
functionalities from the parent class.
@Override is not mandatory for overriding methods. The compiler can
usually figure out if a method is intended to override based on the signature.
However, it's considered good practice to use @Override for better code clarity
and catching potential errors early on.
@Override is not used for implementing methods in interfaces.
Interfaces only declare methods, and subclasses implementing interfaces must
provide their own implementation. The compiler inherently checks for this
relationship.
class User {
private final String firstName;
private final String lastName;
private final String occupation;
public User(String firstName, String lastName, String occupation) {
this.firstName = firstName;
this.lastName = lastName;
this.occupation = occupation;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getOccupation() {
return occupation;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("User{");
sb.append("firstName='").append(firstName).append('\'');
sb.append(", lastName='").append(lastName).append('\'');
sb.append(", occupation='").append(occupation).append('\'');
sb.append('}');
return sb.toString();
}
}
void main() {
User[] users = {
new User("John", "Doe", "gardener"),
new User("Roger", "Roe", "driver"),
new User("Paul", "Smith", "teacher"),
};
for (User user : users) {
System.out.println(user);
}
}
In the example, we override the Object's toString
method. The method is annotated with @Override. If the function
does not match the parent's one (for instance we use private
instead of public) we get a warning:
Cannot reduce the visibility of the inherited method from Object.
The @Deprecated annotation in Java is used to mark classes,
methods, fields, or constructors that are no longer recommended for use. It
serves as a warning to developers that they should avoid using these elements
and consider alternatives.
Key features of the @Deprecated annotation:
@Deprecated, you indicate that it's discouraged from being
used in new code. There might be better alternatives available or the
element might be scheduled for removal in future versions.
@Deprecated, it generates a warning message. This helps
developers identify potential issues and encourages them to migrate to
the suggested alternatives.
@Deprecated annotation is often used
during the evolution of APIs. As libraries and frameworks mature, some
functionalities might become outdated or replaced by more efficient
approaches. Deprecation allows for a gradual transition without breaking
existing code that relies on the old elements.
Additional features of the @Deprecated annotation (Java 9 onwards):
class PassWordGenerator {
@Deprecated
public String generatePassword() {
return "generated password";
}
public String generateSecurePassword() {
return "a secure password";
}
}
// @SuppressWarnings("deprecation")
void main() {
var pgen = new PassWordGenerator();
System.out.println(pgen.generateSecurePassword());
System.out.println(pgen.generatePassword());
}
In the example, we mark the generatePassword with the
@Deprecated annotation, because it is being replaced with a more
secure alternative.
The @Deprecated annotation can be suppressed with
@SuppressWarnings("deprecation") annotation.
We can create our own annotations in Java. Key elements of a custom Java annotation are summarized in the following table:
| Element | Description |
|---|---|
| @interface | Declaration for creating a custom annotation. |
| @Target | Specifies where the annotation can be applied (fields, classes, methods, etc.). |
| @Retention | Determines how long the annotation information is retained (compile-time or runtime). |
| Elements (methods) | Define attributes or parameters for the annotation (with data types and default values). |
| Doc comments | Optional comments to document the annotation and its usage. |
The next example creates a custom annotation.
package com.zetcode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassDescription {
String description();
}
We have a ClassDescription that is targeted on classes.
package com.zetcode;
import java.util.List;
import java.util.Optional;
public class AnnotationProcessor {
public void process(List<Object> objects) {
objects.forEach(obj -> {
Class<?> clazz = obj.getClass();
ClassDescription annotation = clazz.getAnnotation(ClassDescription.class);
// if (annotation != null) {
// System.out.println(annotation.annotationType().getName());
// System.out.println(annotation.description());
// }
Optional.ofNullable(annotation)
.ifPresentOrElse(
ann -> {
System.out.println(ann.annotationType().getName());
System.out.println(ann.description());
},
() -> System.out.println("No annotation found")
);
});
}
}
The AnnotationProcessor will process the annotation.
package com.zetcode;
import java.util.List;
@ClassDescription(description = "this is a User class")
class User {
}
@ClassDescription(description = "this is a Test class")
class Test {
}
class Hello {
}
public class CustomAnnotation {
private String field;
public static void main(String[] args) {
List<Object> objects = List.of(new User(), new Test(), new Hello());
var processor = new AnnotationProcessor();
processor.process(objects);
}
}
We have two classes that are decorated with @ClassDescription. We
process the instance objects with AnnotationProcessor that will
print the description of the decorated class.
In this article we covered annotations in Java. We have shown how to use existing annotations and how to create custom one.
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Java tutorials.