This is the first part of an article devoted to the Java 5+ language mechanism as annotations. It has an introductory character and is designed for Junior developers or those who are just starting to learn a language.
I am
learning Java online and publish some of the training materials as part of the reworking
of the Java Core course .
I also teach
Scala for Java Developers on the udemy.com online education platform (equivalent to Coursera / EdX).
')
My teaching method is that I
- build a complex sequence of examples
- explain possible uses
- explain the logic of moving authors (as far as possible)
- I give a large number of tests (50-100) comprehensively testing understanding and demonstrate various combinations
- I give laboratory for independent work
This article follows points # 1 (a sequence of examples) and # 2 (options).
Go!
Case study: provide user classes with meta-information about “class version”.
Iteration # 1:Just put @ in front of the interface.
public @interface Version {}
Iteration # 2:Annotations may have attributes.
public @interface Version { public int version(); }
Filled when used
@Version(version = 42) public class MyClass {}
The annotation above is completely equivalent to the following (without public). In this, annotations are similar to interfaces: the absence of a scope modifier automatically means public (and not package private as in classes).
public @interface Version { int version(); }
With protected and private - not compiled
public @interface Version { protected int version(); } >> COMPILATION ERROR: Modifier 'protected' not allowed here
Further I will use a variant without the public modifier.
Iteration # 3:If you declare an attribute with the name value, you can omit it when using
public @interface Version { public int value(); }
@Version(42) public class MyClass {}
Although it is possible and in the old manner
@Version(value = 42) public class MyClass {}
Iteration # 4:You can declare default values ​​for an attribute.
public @interface Version { int value(); String author() default "UNKNOWN"; }
Now we have two uses. So
@Version(42) public class MyClass {}
Or like this
@Version(value = 42, author = "Jim Smith") public class MyClass { }
But not like this (listen, shame, yes)
@Version(42, author = "Jim Smith") public class MyClass {} >> COMPILATION ERROR: Annotation attribute must be of the form 'name=value'
Iteration # 5:Attributes may have an array type
public @interface Author { String[] value() default {}; }
@Author({"Anna", "Mike", "Sara"}) public class MyClass {}
But only one dimensional
public @interface Author2D { String[][] value() default {}; } >> COMPILATION ERROR: Invalid type of annotation member
Iteration # 6:A funny trick is possible: annotation - annotation attribute
public @interface Version { int value(); String author() default "UNKNOWN"; }
public @interface History { Version[] value() default {}; }
Applied like this
@History({ @Version(1), @Version(value = 2, author = "Jim Smith") }) public class MyClass {}
Annotations have many limitations. We list some of them.
Restriction: attribute type
1. Attributes can only be of the following types.
- primitives
- String
- Class or "any parameterized invocation of Class"
- enum
- annotation
- array of elements of any of the above types
The last point should be understood as allowing only one-dimensional arrays.
Well, let's act within the limits
Iteration # 7:As an attribute type, you cannot use “regular” Java classes (except for java.lang.String and java.lang.Class), say java.util.Date
import java.util.Date; public @interface Version { Date date(); } >> COMPILATION ERROR: Invalid type for annotation member
But you can emulate records / structures on annotations
public @interface Date { int day(); int month(); int year(); }
public @interface Version { Date date(); }
@Date(year = 2001, month = 1, day = 1) public class MyClass {}
Iteration # 8:An annotation attribute can be enum. From pleasant, it can be declared in the annotation declaration (as in the interface declaration, there may be an enum, class, interface, annotation declaration)
public @interface Colored { public enum Color {RED, GREEN, BLUE} Color value(); }
import static net.golovach.Colored.Color.RED; @Colored(RED) public class MyClass {}
Iteration # 9:An attribute of an annotation can be a class literal.
The version annotation includes a link to the previous version of the class.
public @interface Version { int value(); Class<?> previous() default Void.class; }
First class version
@Version(1) public class ClassVer1 {}
The second version with reference to the first
@Version(value = 2, previous = ClassVer1.class) public class ClassVer2 {}
// Yes, I know that normal people do not include the class version in the class name. But do you know how tedious to invent examples consistent with real practice?
Iteration # 10:A less trivial example with a class literal, where I could not resist and added generics.
The “serializer” interface — one who can write an instance of T to a byte output stream
import java.io.IOException; import java.io.OutputStream; public interface Serializer<T> { void toStream(T obj, OutputStream out) throws IOException; }
Specific "serializer" for the class MyClass
import java.io.IOException; import java.io.OutputStream; public class MySerializer implements Serializer<MyClass> { @Override public void toStream(MyClass obj, OutputStream out) throws IOException { throw new UnsupportedOperationException(); } }
Annotation, with the help of which we "paste the serializer" to a specific class
public @interface SerializedBy { Class<? extends Serializer> value(); }
Well, the MyClass class itself is marked as serialized by “My serializer” MySerializer
@SerializedBy(MySerializer.class) public class MyClass {}
Iteration # 11:Difficult example
public enum JobTitle { JUNIOR, MIDDLE, SENIOR, LEAD, UNKNOWN }
public @interface Author { String value(); JobTitle title() default JobTitle.UNKNOWN; }
public @interface Date { int day(); int month(); int year(); }
public @interface Version { int version(); Date date(); Author[] authors() default {}; Class<?> previous() default Void.class; }
Finally, use annotations.
import static net.golovach.JobTitle.*; @History({ @Version( version = 1, date = @Date(year = 2001, month = 1, day = 1)), @Version( version = 2, date = @Date(year = 2002, month = 2, day = 2), authors = {@Author(value = "Jim Smith", title = JUNIOR)}, previous = MyClassVer1.class), @Version( version = 3, date = @Date(year = 2003, month = 3, day = 3), authors = { @Author(value = "Jim Smith", title = MIDDLE), @Author(value = "Anna Lea")}, previous = MyClassVer2.class) }) public class MyClassVer3 {}
Limitations: Attribute Values ​​— JVM Compile / Load Constants
It should be possible to calculate the values ​​of the annotation attributes at the time of compiling or loading the class in the JVM.
public @interface SomeAnnotation { int count(); String name(); }
Example
@SomeAnnotation( count = 1 + 2, name = MyClass.STR + "Hello" ) public class MyClass { public static final String STR = "ABC"; }
Another example
@SomeAnnotation( count = (int) Math.PI, name = "" + Math.PI ) public class MyClass {}
But method calls are already runtime, this is already prohibited.
@SomeAnnotation( count = (int) Math.sin(1), name = "Hello!".toUpperCase() ) public class MyClass {} >> COMPILATION ERROR: Attribute value must be constant
Conclusion
This is the first part of the article about annotations in Java. In the second part, we will cover the following topics:
- Annotations that modify the behavior of other annotations: @ Target, @ Retention, @ Documented, @ Inherited
- Annotations that modify the behavior of the compiler and the JVM: @ Deprecated, @ Override, @ SafeVarargs, @ SuppressWarnings
- Reading annotations using the Reflection API
Contacts
I do Java online training (here
are the programming courses ) and publish some of the training materials as part of the reworking
of the Java Core course . You can see videos of lectures in the audience on the
youtube channel , perhaps the video of the channel is better organized in
this article .
skype: GolovachCourses
email: GolovachCourses@gmail.com