Annotatsioon
Kirjeldus
Annotatsion on märgend, mis annab kompilaatorile ja Java Virtuaalmasinale lisainfot mingi klassi, meetodi, liidese või välja kohta.
Annotatsioone saab kasutada:
Informatsiooni andmiseks kompilaatorile, mille abil on võimalik kas tuvastada vigu või vaigistada hoiatusi.
Kompileerimise ajal koodi genereerimiseks
Koodi käivitamise ajal info edastamiseks ja käsitlemiseks
Javas on nii sisseehitatud annotatsioone (nt: @Override, @SuppressWarnings, @Deprecated) kui ka on võimalik ise koostada märgendeid.
Sisseehitatud annotatsiooid
@Override
@Override on kõige sagedamini kasutatud annotatsioon. Selle eesmärk on teada anda ning kindlaks teha, et alamklassi meetod kirjutab ülemklassi meetodi üle. Kui mingil põhjusel seda ei juhtu, tekib kompileerimise ajal viga.
class ParentClass {
void method() {
// Do something
}
}
class SubClass extends ParentClass {
@Override
void method() {
// Do something else
}
}
@SuppressWarnings
@SuppressWarnings kasutatakse, et vaigistada kompilaatori poolt antud hoiatusi.
import java.util.*;
class ExampleClass {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
List list = new ArrayList(); // Should throw a compilation warning, but because it's supressed, nothing happens
// Do something with the list
}
}
Antud näites üritame luua List tüüpi objekti ilma talle määramata mingit tüüpi (ehk siis meil on List<String> asemel lihtsalt List). Tavaliselt kompilaator hoiatab meid antud olukorras, kuid kuna meetodil on peal märgend @SuppressWarnings, siis antud hoiatust eiratakse ning vaigistatakse maha.
Mitme hoiatus-tüübi vaigistamiseks tuleb koostada massiiv sõnedest:
@SuppressWarnings({"rawtypes", "deprecated", "unchecked"})
Ideaalis sellist lähenemist võiks vältida ning koodi võiks korda teha, kuid vahest on olukordi, kus me ei saa antud hoiatustega midagi peale hakata.
@Deprecated
@Deprecated märgendiga anname märku, et antud klass/meetod on aegunud ning selle kasutamist võiks vältida. Samuti tuleviks võidakse see koodist ära eemaldada. Antud märgendiga märgitud meetodite kasutamisel kompilaator hoiatab meid.
class ExampleClass {
@Deprecated()
static void oldMethod() {
// Code goes here
}
static void newAndBetterMethod() {
// Code goes here
}
public static void main(String[] args) {
oldMethod();
newAndBetterMethod();
}
}
Antud näites deklaleerisime kaks meetodit, ühele lisasime juurde @Deprecated märgendi. Kui üritada nüüd seda meetodit kasutada, siis kompilaator annab meile sellest teada.
Soovituslik oleks ära märkida ka, mis versioonist alates antud meetod/klass aegunud on ning kas seda kavatsetakse tulevikus ka eemaldada.
@Deprecated(since = "1.1", forRemoval = true)
Annotatsioonide koostamine
Algeline koostamine
Annotatsioone on võimalik ka ise koostada, selleks luuakse liides @interface elemendiga.
@interface MyAnnotation {}
Järgnevalt saame kasutada loodud märgendit sedasi:
@MyAnnotation
void someMethod() {}
Väärtuste lisamine
Märgenditele saame väärtusi juurde lisada järgnevalt
@interface MyAnnotation {
String someStrValue(); // Field with no default value
int valueWithDefault() default 1; // Field with default value
}
Ning kasutada saame järgnevalt:
@MyAnnotation(someStrValue="abc",valueWithDefault=2) // valueWithDefault doesn't have to be called out, because it has a default value
void someMethod() {}
Sisseehitatud kirjeldavad annotatsioonid
Ise loodud märgenditele tasub juurde lisada ka kirjeldavad märgendid. Nendeks on: @Target, @Retention, @Inherited, @Documented
@Target
@Target kasutatakse, et määrata ära millistele osadele programmis antud märgend kehtib. Kui määrata loodud märgend valesse kohta, tekib kompilleerumisviga.
Võimalikud väärtused on: TYPE, FIELD, METHOD, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PARAMETER
ElementType.TYPE - Võimalik kasutada klassi, liidese või enumi kohal
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@interface MyAnnotation {}
// Somewhere in the code
@MyAnnotation
class ExampleClass {}
ElementType.FIELD - Klassisiseste väljade kohal
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@interface MyAnnotation {}
// Somewhere in the code
class ExampleClass {
@MyAnnotation
int value = 10;
}
ElementType.METHOD - Meetodite kohal
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@interface MyAnnotation {}
// Somewhere in the code
class ExampleClass {
@MyAnnotation
void someMethod() {}
}
ElementType.CONSTRUCTOR - Konstruktori kohal
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.CONSTRUCTOR)
@interface MyAnnotation {}
// Somewhere in the code
class ExampleClass {
@MyAnnotation
ExampleClass() {
// Code goes here
}
}
ElementType.LOCAL_VARIABLE - Meetodi sisestel väljadel
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.LOCAL_VARIABLE)
@interface MyAnnotation {}
// Somewhere in the code
class ExampleClass {
void someMethod() {
@MyAnnotation int value = 10;
// Code goes here
}
}
ElementType.ANNOTATION_TYPE - Saab teiste annotatsioonide koostamiseks kasutada
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@interface MyAnnotation {}
// Somewhere in the code
@MyAnnotation
@interface ExampleAnnotation {}
ElementType.PARAMETER - Meetodi parameetrite kohal
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@interface MyAnnotation {}
// Somewhere in the code
class ExampleClass {
void someMethod(@MyAnnotation String input) {}
}
Mitme sihtmärgi deklaleerimiseks tuleb koostada massiiv
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention
@Retention (ehk eluiga) kasutatakse, et määrata ära, millal antud märgendit kasutatakse ning millal see on saadaval.
Võimalikud väärtused on: SOURCE, CLASS, RUNTIME
RetentionPolicy.SOURCE - Kompilaator eirab antud märgendit
RetentionPolicy.CLASS - Märgend salvestatakse klassifaili kompilaatori poolt. Vaikeväärtus
RetentionPolicy.RUNTIME - Märgend salvestatakse klassifaili kompilaatori poolt ning on saadaval ka Java virtuaalmasinas. Reflektsiooniga võimalik hiljem ligi pääseda, sellest hiljem lähemalt.
Näide:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {}
@Inherited
@Inherited märgib ära, et antud annotatsiooni päritakse automaatselt. Kui ülemklass kasutab koostatud märgendit, siis see kandub automaatselt üle ka alamklassidele
Näide:
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Target;
@Inherited
@Target(ElementType.TYPE)
@interface MyAnnotation {}
// Somewhere in the code
@MyAnnotation
class ExampleClassA {
}
// ExampleClassB inhertits @MyAnnotation from ExampleClassA, because we use @Inherited
class ExampleClassB extends ExampleClassA {
}
@Documented
@Documented annab märku, et antud annotatsioon peab olema dokumenteeritud.
Reflektsiooniga funktsionaalsuse lisamine
Kui annotatsioonile on elueaks märgitud RetentionPolicy.RUNTIME, siis sellele on võimalik juurde lisada ka funktsionaalsust, kasutades võtet nimega reflektsioon. Reflektsioon lubab meil juba käima pandud koodi lähemalt uurida ning vajadusel ka seda muutda.
Näite jaoks koostame väikse programmi, millega korrastame inimeste nimesid (Ainult esimene täht suureks, teised väikeseks).
Alustuseks loome annotatsiooni, mida saab ainult väljadel kasutada ning mida saaks ka runtime ajal kasutada:
import java.lang.annotation.*;
@Target(ElementType.FIELD) // This annotation should only be used on fields
@Retention(RetentionPolicy.RUNTIME) // This annotation can be used on runtime
@interface Pretify {}
Järgnevalt koostame klassi, kus sees hoiame inimese eesnime, perenime ning id. Kasutame seal sees oma koostatud märgendit:
class PersonData {
private int id;
@Pretify private String firstName; // First name will be capitalized and corrected
@Pretify private String lastName; // Last name will be capitalized and corrected
public PersonData(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return "My name is: " + firstName + " " + lastName + ", id: " + id;
}
}
Viimaseks kirjutame meetodi, mis otsib klassist üles väljad antud annotatsiooniga ning korrastab need ära. Antud võtte nimi on reflektsioon - analüüsime juba tööle pandud koodi ning muudame seda reaalajas. NB: Autor kirjutas meetodi Example klassi
import java.lang.reflect.Field;
public static void pretifyName(PersonData data) {
Field[] fields = data.getClass().getDeclaredFields(); // Gets declared fields from class
for (Field field : fields) {
Pretify annotation = field.getAnnotation(Pretify.class); // Tries to get annotation from field
if (annotation == null) { // If annotation wasn't found, continue to next field
continue;
}
try {
field.setAccessible(true); // Make it so we can modify that field
String value = field.get(data).toString().toLowerCase(); // Get value from field
value = value.substring(0, 1)
.toUpperCase() + value.substring(1); // Make first letter uppercase, others lowercase
field.set(data, value); // Set new value to field
field.setAccessible(false); // Make it back to private
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Lõpuks koostame main meetodi ning kontrollime, kas töötab:
import java.util.Arrays;
import java.util.List;
public class Example {
public static void main(String[] args) {
List<PersonData> list = Arrays.asList(
new PersonData(1, "persON", "oNe"),
new PersonData(2, "nUmBEr", "TWO"),
new PersonData(3, "thirD", "persoN")
);
list.forEach(Example::pretifyName);
list.forEach(System.out::println);
}
// Previously written method somewhere here
}
Ning väljundiks saime korrastatud nimed:
My name is: Person One, id: 1
My name is: Number Two, id: 2
My name is: Third Person, id: 3
Lisaks soovitan ka korra muuta RetentionPolicy RUNTIME pealt millekski muuks ning võrrelda tulemusi.