📜 ⬆️ ⬇️

Java Annotations, Part I

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
  1. build a complex sequence of examples
  2. explain possible uses
  3. explain the logic of moving authors (as far as possible)
  4. I give a large number of tests (50-100) comprehensively testing understanding and demonstrate various combinations
  5. 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.

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:


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

Source: https://habr.com/ru/post/217595/


All Articles