...

понедельник, 31 марта 2014 г.

Аннотации в Java, часть I

Это первая часть статьи, посвященной такому языковому механизму Java 5+ как аннотации. Она имеет вводный характер и рассчитана на Junior разработчиков или тех, кто только приступает к изучению языка.

Я занимаюсь онлайн обучением Java и опубликую часть учебных материалов в рамках переработки курса Java Core.

Мой метод обучения состоит в том, что я



  1. строю усложняющуюся последовательность примеров

  2. объясняю возможные варианты применения

  3. объясняю логику двигавшую авторами (по мере возможности)

  4. даю большое количество тестов (50-100) всесторонне проверяющее понимание и демонстрирующих различные комбинации

  5. даю лабораторные для самостоятельной работы




Данная статье следует пунктам #1 (последовательность примеров) и #2(варианты применения).



Поехали!




Учебный пример: снабдить классы пользователя мета-информацией о «версии класса».

Итерация #1:

Просто ставим @ перед interface.



public @interface Version {}


Итерация #2:

У аннотаций могут быть атрибуты.



public @interface Version {
public int version();
}




И заполнять их при использовании аннотации

@Version(version = 42)
public class MyClass {}




Аннотация выше полностью эквивалентна следующей (без public). В этом аннотации эквивалентны интерфейсам: отсутствие модификатора области видимости автоматически означает public (а не package private как у классов).

public @interface Version {
int version();
}




С protected и private — не компилируется

public @interface Version {
protected int version();
}
>> COMPILATION ERROR: Modifier 'protected' not allowed here




Далее я буду использовать вариант без модификатора public

Итерация #3:

Если объявить атрибут с именем value, то его можно опускать при использовании



public @interface Version {
public int value();
}



@Version(42)
public class MyClass {}




Хотя можно и по старинке

@Version(value = 42)
public class MyClass {}


Итерация #4:

Для атрибута можно объявить значения по умолчанию



public @interface Version {
int value();
String author() default "UNKNOWN";
}




Теперь у нас два варианта использования. Так

@Version(42)
public class MyClass {}




Или вот так

@Version(value = 42, author = "Jim Smith")
public class MyClass {
}




Но не вот так (слушай, обидно, да)

@Version(42, author = "Jim Smith")
public class MyClass {}
>> COMPILATION ERROR: Annotation attribute must be of the form 'name=value'


Итерация #5:

Атрибуты могут иметь тип массива



public @interface Author {
String[] value() default {};
}



@Author({"Anna", "Mike", "Sara"})
public class MyClass {}




Но только одномерного

public @interface Author2D {
String[][] value() default {};
}
>> COMPILATION ERROR: Invalid type of annotation member


Итерация #6:

Возможен забавный трюк: аннотация — атрибут аннотации



public @interface Version {
int value();
String author() default "UNKNOWN";
}



public @interface History {
Version[] value() default {};
}




Применяется вот так

@History({
@Version(1),
@Version(value = 2, author = "Jim Smith")
})
public class MyClass {}


У аннотаций много ограничений. Перечислим некоторые из них.


Ограничение: тип атрибута




1. Атрибуты могут иметь только следующие типы


  • примитивы

  • String

  • Class или «any parameterized invocation of Class»

  • enum

  • annotation

  • массив элементов любого из вышеперечисленных типов




Последний пункт надо понимать как то, что допустимы только одномерные массивы.

Ну что же, давайте действовать в рамках ограничений


Итерация #7:

В качестве типа атрибута нельзя использовать «обычные» классы Java (за исключением java.lang.String и java.lang.Class), скажем java.util.Date



import java.util.Date;

public @interface Version {
Date date();
}
>> COMPILATION ERROR: Invalid type for annotation member




Но можно эмулировать записи/структуры на аннотациях

public @interface Date {
int day();
int month();
int year();
}



public @interface Version {
Date date();
}



@Date(year = 2001, month = 1, day = 1)
public class MyClass {}


Итерация #8:

Атрибутом аннотации может быть enum. Из приятного, его можно объявить в объявлении аннотации (как и в объявлении интерфейса тут может быть объявление enum, class, interface, annotation)



public @interface Colored {
public enum Color {RED, GREEN, BLUE}
Color value();
}



import static net.golovach.Colored.Color.RED;

@Colored(RED)
public class MyClass {}


Итерация #9:

Атрибутом аннотации может быть классовый литерал.

Аннотация версии включает ссылку на предыдущую версию класса.



public @interface Version {
int value();
Class<?> previous() default Void.class;
}




Первая версия класса

@Version(1)
public class ClassVer1 {}




Вторая версия со ссылкой на первую

@Version(value = 2, previous = ClassVer1.class)
public class ClassVer2 {}




// Да, я знаю, что нормальные люди не включают версию класса в имя класса. Но знаете как нудно придумывать примеры согласованные с реальной практикой?

Итерация #10:

Менее тривиальный пример с классовым литералом, где я не удержался и добавил generic-ов.

Интерфейс «сериализатора» — того, кто может записать экземпляр T в байтовый поток вывода



import java.io.IOException;
import java.io.OutputStream;

public interface Serializer<T> {
void toStream(T obj, OutputStream out) throws IOException;
}




Конкретный «сериализатор» для класса 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();
}
}




Аннотация, при помощи которой мы «приклеиваем сериализатор» к конкретному классу

public @interface SerializedBy {
Class<? extends Serializer> value();
}




Ну и сам класс MyClass отмеченный, как сериализуемый «своим сериализатором» MySerializer

@SerializedBy(MySerializer.class)
public class MyClass {}


Итерация #11:

Сложный пример



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;
}




Ну и наконец использование аннотации

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 = {@_8_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 {}


Ограничение: значения атрибутов — константы времени компиляции/загрузки JVM




Должна быть возможность вычислить значения атрибутов аннотаций в момент компиляции или загрузки класса в JVM.

public @interface SomeAnnotation {
int count();
String name();
}




Пример

@SomeAnnotation(
count = 1 + 2,
name = MyClass.STR + "Hello"
)
public class MyClass {
public static final String STR = "ABC";
}




Еще пример

@SomeAnnotation(
count = (int) Math.PI,
name = "" + Math.PI
)
public class MyClass {}




А вот вызовы методов — это уже runtime, это уже запрещено

@SomeAnnotation(
count = (int) Math.sin(1),
name = "Hello!".toUpperCase()
)
public class MyClass {}
>> COMPILATION ERROR: Attribute value must be constant


Заключение




Это первая часть статьи про аннотации в Java. Во второй части мы рассмотрим следующие темы:


  • Аннотации, модифицирующие поведение других аннотаций: @ Target, @ Retention, @ Documented, @ Inherited

  • Аннотации, модифицирующие поведение компилятора и JVM: @ Deprecated, @ Override, @ SafeVarargs, @ SuppressWarnings

  • Чтение аннотаций с помощью Reflection API


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий