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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With