Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do user annotations work?

I am still fairly new to Java programming and I was looking over a open source project and came across this

public @TileNetworkData int progressPart = 0;   

I have seen the use of @ before but only to do things like @override before a member. To my surprise looking up the definition it brought me to user code

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TileNetworkData {

    int staticSize () default -1; 

}

What is this code doing, and what is it useful for? It looks like it is adding some form of metadata to the field. How is something like this used?

Doing some Googleing I found this is called "Annotations" but everything attached to it went over my head. Any kind of example where something like this would be used would be appreciated.

like image 670
Scott Chamberlain Avatar asked Feb 02 '23 13:02

Scott Chamberlain


2 Answers

Annotations are used as "machine-readable metadata" - they describe the fields, methods and classes they label in a way that the compiler and runtime can parse, and possibly even understand. If you are familiar with .NET's attributes, you'll find that Java annotations are used similarly.

For example, the TileNetworkData annotation defined in your example is itself decorated with an annotation Retention(RetentionPolicy.RUNTIME). This tells the compiler to embed the TileNetworkData annotation into the bytecode for the fields it annotates. The same annotation also tells the Java runtime that, when it loads classes with TileNetworkData-annotated fields, it should retain the TileNetworkData annotation for runtime reflection.

Now, your code can reflect over fields of an object to find the TileNetworkData annotations and do something with fields so annotated:

// I haven't even compiled this code, and even if it works, it's still insane.
// (others who know annotations better should feel free to edit or replace)
public void copyTileNetworkDataToCache (Object data, Cache<?> cache) {
  for (Field f : data.getClass().getFields()) {
    if (f.isAnnotationPresent(TileNetworkData.class)) {
      cache.save(f.get(data));
    }
  }
}

You can even write code that teaches the Java compiler how to interpret your annotations at compile time, using the apt front end in JDK 5 and javac switches in JDK 6 and later. To make up another lame example, accessing the tile network might take so long that you want to avoid using data from it whenever possible. Hence, you might want to make a list of all classes that include TileNetworkData-annotated fields, so you can review them all and possibly rewrite the ones that don't absolutely need to access the tile network. To do this, you might write an annotation processor that prints out all matching classes, then point apt at the processor when you compile.

like image 178
Michael Ratanapintha Avatar answered Feb 20 '23 06:02

Michael Ratanapintha


Example: Transaction demarcation:

public class TransactionalThing {
    @Transactional
    public void doSomePersistenceStuff() {
        Foo foo = loadAFoo();
        doSomeStuffWith(foo);
        foo.setProcessed(true);
    }
}

There would be other code that looks for methods annotated by @Transactional, starts a transaction before the method is called, and commits (or rolls back) the transaction when it finishes. You can also put information in the annotation about things like, for example, rollback rules:

@Transactional(rollbackFor = SomeException.class,
               noRollbackFor = SomeOtherException.class)

And again, it's just up to the code that scans these transactions to read in those attributes and handle things appropriately.

like image 33
Ryan Stewart Avatar answered Feb 20 '23 06:02

Ryan Stewart