How and Where Are Annotations Used in Java

How and where are Annotations used in Java?

Annotations are meta-meta-objects which can be used to describe other meta-objects. Meta-objects are classes, fields and methods. Asking an object for its meta-object (e.g. anObj.getClass() ) is called introspection. The introspection can go further and we can ask a meta-object what are its annotations (e.g. aClass.getAnnotations). Introspection and annotations belong to what is called reflection and meta-programming.

An annotation needs to be interpreted in one way or another to be useful. Annotations can be interpreted at development-time by the IDE or the compiler, or at run-time by a framework.

Annotation processing is a very powerful mechanism and can be used in a lot of different ways:

  • to describe constraints or usage of an element: e.g. @Deprecated, @Override, or @NotNull
  • to describe the "nature" of an element, e.g. @Entity, @TestCase, @WebService
  • to describe the behavior of an element: @Statefull, @Transaction
  • to describe how to process the element: @Column, @XmlElement

In all cases, an annotation is used to describe the element and clarify its meaning.

Prior to JDK5, information that is now expressed with annotations needed to be stored somewhere else, and XML files were frequently used. But it is more convenient to use annotations because they will belong to the Java code itself, and are hence much easier to manipulate than XML.

Usage of annotations:

  • Documentation, e.g. XDoclet
  • Compilation
  • IDE
  • Testing framework, e.g. JUnit
  • IoC container e.g. as Spring
  • Serialization, e.g. XML
  • Aspect-oriented programming (AOP), e.g. Spring AOP
  • Application servers, e.g. EJB container, Web Service
  • Object-relational mapping (ORM), e.g. Hibernate, JPA
  • and many more...

...have a look for instance at the project Lombok, which uses annotations to define how to generate equals or hashCode methods.

What is the purpose of annotations in Java?

Annotations are metadata.
@Override annotation is used to make sure that you are overriding method of a superclass and not just making a method with the same name. Common mistakes here consist of:

  • spelling the method's name wrong
    equal(Object o) instead of equals(Object o)
  • putting different set of arguments

    MyString extends String { public boolean equals(MyString str) {} }

equals(MyString str) is not overriding the method equals(Object o) and therefore will not be used by standard Java comparators (which is used in some standard functions, such as List.contains() and this is prone to error situation).
This annotation helps compiler to ensure that you code everything correctly and in this way it helps program.

@Deprecated annotation doesn't make program not to compile but it makes developers think about using the code that can and/or will be removed in a future releases. So they (developers) would think about moving onto another (updated) set of functions. And if you compile your program with the flag -Xlint compilation process will return with an error unless you remove all usages of deprecated code or explicitly mark them with annotation @SuppressWarnings("deprecation").

@SuppressWarnings is used to suppress warnings (yes, I know it's Captain Obvious style :)). There is a deprecation suppression with @SuppressWarnings("deprecation"), unsafe type casting with @SuppressWarnings("unchecked") and some others. This is helpfull when your project compiler have a compilation flag -Xlint and you cannot (or don't want to) change that.

There are also annotation processors that you integrate into your program build process to ensure that program code meets some sort of criteria. For example with IntelliJ Idea IDE annotation processor you can use @Nullable and @NotNull annotations. They show other programmers when they use your code so that can transfer null as a certain parameter to a method or not. If they transfer null it will cause exception during compilation or before executing a single line method's code.

So annotations are quite helpful if you use them to their full potential.

Why java annotations?

When compared to marker interfaces, annotations have some advantages:

  • they can be parameterized
  • they are more fine grained - you can attach them not only to classes but also to other class elements (fields, methods, method arguments, etc)

Annotations are also supposedly less intrusive, but this point is matter of taste and debatable.

See also:

  • Annotations (official JDK documentation)
  • What is the use of marker interfaces in Java?

What do annotations do? And how do I use it?

Annotations don't do anything by themselves. They are some attributes which are put on some other code structs (such as methods, classes, arguments,...). There must be some other processor to read the annotations and do something.

Some annotations are just in source codes, and are not put in the compiled code, so tools which process the source code read them and process them, some annotations are put in compiled code but are not loaded by class loader, and some are put in compiled code and are loaded by class loader, so they can be read in runtime (these 3 behaviors are distinguished by @Retention annotation on the annotation itself).

As far as I know in Java Language Specification, it is mentioned that lack of annotation classes on runtime shall not stop the application working, it means the class-loader just to ignore them. But may be the processor, or framework which use them make errors (which is what they should do).

How do annotations like @Override work internally in Java?

The first main distinction between kinds of annotation is whether they're used at compile time and then discarded (like @Override) or placed in the compiled class file and available at runtime (like Spring's @Component). This is determined by the @Retention policy of the annotation. If you're writing your own annotation, you'd need to decide whether the annotation is helpful at runtime (for autoconfiguration, perhaps) or only at compile time (for checking or code generation).

When compiling code with annotations, the compiler sees the annotation just like it sees other modifiers on source elements, like access modifiers (public/private) or final. When it encounters an annotation, it runs an annotation processor, which is like a plug-in class that says it's interested a specific annotation. The annotation processor generally uses the Reflection API to inspect the elements being compiled and may simply run checks on them, modify them, or generate new code to be compiled. @Override is an example of the first; it uses the Reflection API to make sure it can find a match for the method signature in one of the superclasses and uses the Messager to cause a compile error if it can't.

There are a number of tutorials available on writing annotation processors; here's a useful one. Look through the methods on the Processor interface for how the compiler invokes an annotation processor; the main operation takes place in the process method, which gets called every time the compiler sees an element that has a matching annotation.

Understanding annotation in Java

Annotations are basically bits of data you can attach to fields, methods, classes, etc.

The syntax for declaring annotations in Java is a little awkward. They look a bit like interfaces (they are, after all, declared with @interface), but they aren't really interfaces. I think you might have put the doTestTarget() method in your TestAnnotations class because you thought your annotation was an interface and you needed to implement it. This isn't true - you can delete this method and the call to it from your code if you wish and doing so won't cause you any problems.

Also, you might not have intended to put the annotation on the field str. Annotations apply only to what immediately follows them. As a result, your code doesn't compile, because you've applied your annotation to a field but declared that your annotation can only be applied to methods. Change @Target(ElementType.METHOD) to @Target(ElementType.FIELD) and your code should then compile.

As for what happens to the string Hello World !, it gets written to the .class file and is available to any tool that reads in Java classes. However, it wouldn't necessarily be available in the JVM at runtime. This happens because you didn't specify a @Retention for your @Test_Target annotation. The default value for @Retention is RetentionPolicy.CLASS, which means that the JVM might not bother to load them out of the class file. (See the Javadoc for the RetentionPolicy enum.)

I imagine you want to see some way of reading the value out of this annotation at runtime. If so, I'd recommend adding @Retention(RetentionPolicy.RUNTIME) to your annotation to make sure it will be available at runtime.

To access your annotation and the value contained within it at runtime, you need to use reflection. I've rewritten your TestAnnotations class as follows to give a quick demonstration:

import java.lang.reflect.Field;

public class TestAnnotations {

@Test_Target(doTestTarget="Hello World !")
private String str;

public static void main(String[] args) throws Exception {
// We need to use getDeclaredField here since the field is private.
Field field = TestAnnotations.class.getDeclaredField("str");
Test_Target ann = field.getAnnotation(Test_Target.class);
if (ann != null) {
System.out.println(ann.doTestTarget());
}
}
}

When I run this code, it gives me the following output:


Hello World !

Is it possible to have a Java annotation that doesn't apply to any class, method, field, etc. Just the annotation itself generating code

You cannot have a stand-alone annotation in Java.

Annotations can be applied to different things, for example: types, methods, fields, local variables, packages, method parameters and also on annotation definitions.

One annotation that is meant to be used on annotation definitions (therefore it's called a "meta-annotation") is @Target, which you use to indicate on what things the annotation you are defining is allowed to be used. You do this by specifying one or more element types as an argument to the @Target annotation - see the API docs of java.lang.annotation.ElementType.

The Java Language Specification paragraph 9.6.4.1 explains what annotations can be used on in more detail.

in java what does the @ symbol mean?

The @ symbol denotes a Java Annotation. What a Java annotation does, is that it adds a special attribute to the variable, method, class, interface, or other language elements. (This can be configured when you declare the annotation) When you add an annotation to something, other parts of the program can check whether something has an annotation or not. It then can use this information to do whatever stuff they need.

Let me give you some examples:

The @Override annotation

public class SuperClass {
public void someInterestingMethod() {
System.out.println("Superclass!");
}
}

public class DerivedClass extends SuperClass {
public void someInterestngMethod() {
System.out.println("Derived class!");
}
}

And when you do this:

SuperClass sc = new DerivedClass();
sc.someInterestingMethod();

The someInterestingMethod() call should be dynamically dispatched, and print "Derived class!", right? Well the derived class' method was actually misspelled, so DerivedClass got its own separate method called someInterestngMethod(), totally unrelated to the superclass' someInterestingMethod(). As such, someInterestingMethod() is no longer overridden, and the superclass' implementation is invoked.

The @Override keyword is intended to help with this. It signals your intent to the compiler, that you would like the annotated method to be an overload of one of the ancestor class' methods. If it's not (such as in this typo case, or if the SuperClass API changed and renamed the method), the will fail your compilation, to alert your attention to the broken override.

The @SuppressWarnings Annotation

Here is a method:

public void someMethod() {
int i;
}

There will be a compiler warning saying that i is never used. So you can add the @SuppressWarnings to the method to suppress the warning:

@SuppressWarnings("unused")
public void someMethod() {
int i;
}

Note that there is a parameter to the @SuppressWarnings annotation. Some annotations have parameters and you can look for the them in the javadoc. But for those that don't have parameters you don't need to add () like a method.

You can also declare your own annotations and use reflection to check for them. The above 2 annotations will be checked by the compiler.

What's the point of package annotations?

  1. bnd tool (and maven-bundle-plugin) makes use of package annotations. Putting @Version and @Export annotation in package-info.java allows it to generate OSGi metadata.
  2. javadoc uses package annotations.
  3. JAXB uses package-level annotations, for example, to specify mapping of a Java type to XML Schema type package-wide. Package annotations are also used in JBoss's xml binding.
  4. Struts 2 Convention plugin uses an annotation to specify a default interceptor for all actions in a package.
  5. There are some package-level Hibernate Annotations. An example of those annotations' usage can be found here.

Can an annotation type without a @Target be used anywhere?

It depends on Java version. Java SE 18 says

If an @Target meta-annotation is not present on an annotation
interface T, then an annotation of type T may be written as a modifier
for any declaration.

Unfortunately https://javaalmanac.io does not do diffs at the level of detail necessary to find out in which version it changed.



Related Topics



Leave a reply



Submit