Liigu peamise sisu juurde

Sisseehitatud annotatsioonid

Sissejuhatus

Annotatsioon on märgis, mida saab lisada Java programmi elementidele - klassidele, meetoditele, väljadele ja parameetritele - et siduda neile mingit lisainfot. Süntaks on @Nimi, mis kirjutatakse elemendi kohale või kõrvale (vasakule).

Iseenesest annotatsioonid ei tee midagi, need on puhtalt metaandmed. Neile annab tähenduse kas:

  • kompilaator, mis kontrollib mõnda annotatsiooni ning väljastab vigu või hoiatusi
  • raamistik või teek, mis loeb annotatsioone programmi töö ajal reflektsiooni abil ning muudab nende põhjal oma käitumist.

Enne oma annotatsioonide defineerimist tasub tutvuda nendega, mida standardteek juba pakub. Need illustreerivad mõlemat lähenemist - kompilaatori- ja käitusajapõhist - ning neid leidub enamikust Java koodist igapäevaselt.

@Override

@Override märgib, et meetod on mõeldud ülekirjutama (või implementeerima) ülemklassis või liideses deklareeritud meetodit:

public class Book {
private final String title;

@Override
public String toString() {
return "Book{" + title + "}";
}
}

Kompilaator kontrollib, et vastav ülekirjutatav meetod eksisteeriks täpselt sama signatuuriga. Kui kirjutad nime valesti või eksid parameetritega, siis kompileerimine ebaõnnestub:

@Override
public String toSring() { // typo - no such method to override
...
}
// error: method does not override or implement a method from a supertype

@Override on praktikas kõige levinum annotatsioon ning üks väheseid, mille kasu avaldub täielikult kompileerimise ajal - see ei mõjuta programmi käitusajal.

@Deprecated

@Deprecated märgib, et antud klassi, meetodit, välja või konstruktorit ei tohiks tulevikus enam kasutada. Kui sellega märgitud elementi kasutada, siis kompilaator väljastab vastava hoiatuse.

public class BookService {

@Deprecated(since = "2.0", forRemoval = true)
public Book findById(int id) {
return findByIsbn(String.valueOf(id));
}

public Book findByIsbn(String isbn) {
...
}
}

Sellele kuuluvad ka kaks valikulist parameetrit:

  • since dokumenteerib versiooni, kus element kuulutati aegunuks
  • forRemoval = true annab märku, et element eemaldatakse tulevases versioonis. Selle märkimisel väljastab kompilaator tugevama hoiatuse (sisuliselt, et antud elementi ei tohiks kasutada ning peaks ära asendama)

@Deprecated on suhtlusvahend teegi autorite ja teegi kasutajate vahel. Olemasolev kood jätkab töötamist, see saab lihtsalt nähtava hoiatuse.

@SuppressWarnings

@SuppressWarnings annab kompilaatorile märku, et see vaigistaks kindlaid hoiatusi elemendil, millele annotatsioon on lisatud.

@SuppressWarnings("unchecked")
public <T> List<T> castList(List<?> list) {
return (List<T>) list;
}

Levinud väärtused on näiteks "unchecked", "deprecation" ja "rawtypes". Annotatsiooni ulatus peaks olema võimalikult kitsas. Selle lisamine tervele klassile võib peita hoiatusi, mis ei ole seotud sellega, mida tegelikult vaigistada sooviti.

hoiatus

Ideaalis ei peaks seda annotatsiooni kunagi kasutama - hoiatused eksisteerivad põhjusega. Kui kood tekitab hoiatuse, siis on parem selle algpõhjus parandada, kui seda vaigistada. @SuppressWarnings on õigustatud ainult siis, kui oled veendunud, et hoiatus on väärpositiivne ning koodi pole võimalik ümber kirjutada nii, et hoiatust ei tekiks.

@SuppressWarnings on puhtalt vihje kompilaatorile. Sellel puudub täielikult mõju programmi töö ajal.

@FunctionalInterface

@FunctionalInterface märgib, et antud liidesel peab olema täpselt üks abstraktne meetod - see nõue tagab, et liidest saaks kasutada lambda-avaldistes:

@FunctionalInterface
public interface BookFilter {
boolean accepts(Book book);
}

Kui liidesele lisatakse teine abstraktne meetod, väljastab kompilaator vea. See kaitseb funktsionaalseid liideseid selle eest, et neid kogemata täiendustega ära ei rikutaks. Funktsionaalsete liideste kohta saate lähemalt lugeda siit.

Sarnaselt @Override-ile, on ka see ainult kompileerimisajal tehtav kontroll.

Annotatsioonid annotatsioonide jaoks ehk metaannotatsioonid

Mõned annotatsioonid eksisteerivad ainult selleks, et neid kasutada teiste annotatsioonide peal - need kirjeldavad, kuidas annotatsioon ise käituma peaks:

  • @Retention - kas annotatsioon säilib pärast kompileerimist ning kas see on programmi töö ajal nähtav.
  • @Target - millist tüüpi elementidele (klass, meetod, väli, parameeter jne) annotatsiooni võib lisada.
  • @Documented - kas annotatsioon peaks ilmuma genereeritud Javadocis.
  • @Inherited - kas alamklass pärib annotatsiooni automaatselt oma ülemklassilt.

Neid käsitleme täpsemalt Käsitsi loodud annotatsioonide peatükis, sest just seal neid tegelikult kasutatakse.

Kokkuvõte

Kõik ülaltoodud annotatsioonid on programmi vaatenurgast passiivsed. @Override ja @FunctionalInterface on kompileerimisaja kontrollid, @Deprecated tekitab hoiatusi, @SuppressWarnings vaigistab neid. Ükski neist ei osale programmi tööloogikas käitamise ajal.

Huvitavam on sissejuhatuses mainitud kolmas kategooria: annotatsioonid, mida raamistik loeb programmi töö ajal ning mille põhjal tehaakse midagi. Näiteks @Test JUnitis, @JsonProperty Jacksonis või @Autowired Springis. Nende ehitamiseks tuleb annotatsioone ise defineerida, millega tutvume lähemalt järgmises peatükis.