Java napomene — ne potcenjuju ih.
„Erge, danas ćemo govoriti o napomenama?" pita Sanmej.
„Da." kažem. „Napomene su veoma važan deo Jave, ali često se zanemaruju, to je stvarno tako. Razlog za to je što smo skloniji da budemo korisnici napomena nego njihovi kreatori. Siromašta koristio @Override napomenu? Koristio si je pri redefinisanju metoda. Ali znaš li da definišeš sopstvenu napomenu?"
Sanmej bez oklevanja protresa glavom, okreće rukom i stidi da prizna da zaista nije nikada sama definisala napomenu.

„Vredi, kažu ti ja."
Napomena (Annotation) je koncept uveden u Javi 1.5, isto kao class i interface, predstavlja jedan tip. Napomene pružaju niz podataka koji se koriste za dekorisanje programskog koda (klase, metode, polja itd.), ali napomena nije deo dekorisanog koda, nema direktan uticaj na izvršavanje koda, odluku o tome koje operacije će se izvršiti donosi kompajler.
Da pogledamo jedan deo koda.
public class AutowiredTest {
@Autowired
private String name;
public static void main(String[] args) {
System.out.println("Chenmo Wang Er, zanimljiv programer");
}
}Primetio si @Autowired napomenu? Ona je u suštini namenjena za Spring (ćeš kasnije naučiti) kontejner za ubrizgavanje Bean-a, sad sam je bezdušno bacio na polje name, ali ovaj projekat nije uključio Spring, što znači da @Autowired napomena u ovom trenutku služi samo kao ukras.
„Pošto je samo ukras, zašto ga onda uopšte koristiš @Autowired?" radoznosno pita Sanmej.
„Glupa si, to sam ti dao kao primer, da dokažem: napomene nemaju direktan uticaj na izvršavanje koda, razumeš moju nameru?" bez dlake kažem.
„Oh." Sanmej misli.
„Pažljivo slušaj, sada ću ti objasniti životni ciklus napomena." pogledam Sanmej, vidim da li pažljivo sluša, zatim nastavljam, „Životni ciklus napomena ima 3 strategije, definisane u enumeraciji RetentionPolicy."
SOURCE: Važi u izvornom fajlu, biva se odbacuje od strane kompajlera.
CLASS: Važi u bajtkod fajlu generisanom od strane kompajlera, ali se prilikom izvršavanja odbacuje od strane JVM-a koji procesira fajl klase.
RUNTIME: Važi prilikom izvršavanja. Ovo je najčešća strategija životnog ciklusa napomena, omogućuje programu da pristupa napomenama putem refleksije i izvršava odgovarajući kod na osnovu definicije napomene.
„Zatim ćemo pričati o ciljevima dekorisanja napomenama." vidim da se Sanmej još uvek nalazi na vezi, nastavljam.
Cilj napomene definiše na kojem nivou Java koda će napomena biti primenjena, neke napomene važe samo za metode, neke samo za članske promenljive, neke samo za klase, a neke za sve. Do Jave 9, postojalo je 11 tipova napomena, definisanih u enumeraciji ElementType.
TYPE: Za klase, interfejse, napomene, nabrajanja
FIELD: Za polja (članske promenljive klase) ili konstante nabrajanja
METHOD: Za metode
PARAMETER: Za parametre običnih metoda ili konstruktora
CONSTRUCTOR: Za konstruktore
LOCAL_VARIABLE: Za promenljive
ANNOTATION_TYPE: Za napomene
PACKAGE: Za pakete
TYPE_PARAMETER: Za parametre generika
TYPE_USE: Za tipove u naredbama deklaracije, generika ili naredbikast tipova
MODULE: Za module
„Brate, zar ne možeš direktno da me naučiš kako da se koriste napomene, već da me toliko mučiš!!!!!" Sanmej je postala netrpeljiva.
„Stvarno, više priče nego što se uopiš, bolje je napraviti jednu napomenu koja će te oduševiliti." nasmejem se na Sanmej. „Kako ti se čini?"
„Super!"
„Evo pogledajte sledeći deo koda."
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
public String value() default "";
}Životni ciklus napomene JsonField je RUNTIME, tj. važi prilikom izvršavanja.
Cilj napomene JsonField je FIELD, tj. odnosi se na polja.
Za kreiranje napomene potrebna je
@interfaceključna reč.Napomena JsonField ima jedan parametar naziva value, tipa String, podrazumevana vrednost je prazan string.
„Zašto parametar treba da se zove value? Ima li to neko specijalno značenje?" pita Sanmej.
„Naravno da ima, value omogućava korisniku napomene da prosledi parametar bez navođenja imena. Na primer, na polju možemo koristiti @JsonField(value = "Chenmo Wang Er"), ali možemo i izostaviti value =, pa postaje @JsonField("Chenmo Wang Er")." kažem.
„A šta default "" znači? Neki specijalan efekat?" nastavlja Sanmej.
„Naravno da ima, to omogućava da direktno na polju koristimo @JsonField, bez navođenja imena i vrednosti parametra." odgovaram.
„Razumem, pa sad @JsonField napomena je napravljena, može li se koristiti?" uzbuđuje se Sanmej.
„Da, pretpostavimo da imamo Writer klasu sa 3 polja: age, name i bookName, gde su poslednja dva obavezna polja za serijalizaciju. Tako se može koristiti @JsonField napomena." kažem.
public class Writer {
private int age;
@JsonField("writerName")
private String name;
@JsonField
private String bookName;
public Writer(int age, String name, String bookName) {
this.age = age;
this.name = name;
this.bookName = bookName;
}
// getter / setter
@Override
public String toString() {
return "Writer{" +
"age=" + age +
", name='" + name + '\'' +
", bookName='" + bookName + '\'' +
'}';
}
}@JsonFieldna polju name navodi eksplicitnu string vrednost.@JsonFieldna polju bookName koristi podrazumevanu vrednost.
Zatim ćemo napisati klasu za serijalizaciju JsonSerializer, sadržaj je sledeći:
public class JsonSerializer {
public static String serialize(Object object) throws IllegalAccessException {
Class<?> objectClass = object.getClass();
Map<String, String> jsonElements = new HashMap<>();
for (Field field : objectClass.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(JsonField.class)) {
jsonElements.put(getSerializedKey(field), (String) field.get(object));
}
}
return toJsonString(jsonElements);
}
private static String getSerializedKey(Field field) {
String annotationValue = field.getAnnotation(JsonField.class).value();
if (annotationValue.isEmpty()) {
return field.getName();
} else {
return annotationValue;
}
}
private static String toJsonString(Map<String, String> jsonMap) {
String elementsString = jsonMap.entrySet()
.stream()
.map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
.collect(Collectors.joining(","));
return "{" + elementsString + "}";
}
}„Sad je sadržaj klase JsonSerializer malo težak za razumevanje, Erge, malo teško razumem." kaže Sanmej.
„Nemoj se plasiti, polako ću ti objasniti, dok ga potpuno ne razumeš."
serialize()metod se koristi za serijalizaciju objekata, prima parametar tipa Object.objectClass.getDeclaredFields()kroz refleksiju dobija sva polja deklarisana u objektu, zatim prolazi kroz for petlju. U for petlji, prvo se krozfield.setAccessible(true)postavlja dostupnost refleksionog objekta na true, kako bi se moglo koristiti za serijalizaciju (bez ovog koraka ne bi moglo da se pristupiti privatnim poljima, biće bačen IllegalAccessException izuzetak); zatim krozisAnnotationPresent()proverava da li je na polju dekorisana@JsonFieldnapomena, ako jeste, pozivagetSerializedKey()metod i dobija vrednost objekta predstavljenog tim poljem, i stavlja ga u jsonElements.getSerializedKey()metod se koristi za dobijanje vrednosti napomene na polju, ako je vrednost napomene prazna, vraća ime polja.toJsonString()metod koristi Stream tok da vrati formatirani JSON string. Stream tok još nisi nailazio, ali ne brini, kasnije ću ti ga objasniti.
„Sada je sve jasno?" pitam Sanmej, videći da ona kimće, nastavljam: „Sada ćemo napisati testnu klasu JsonFieldTest."
public class JsonFieldTest {
public static void main(String[] args) throws IllegalAccessException {
Writer cmower = new Writer(18,"Chenmo Wang Er","Web Full Stack Development Advanced Road");
System.out.println(JsonSerializer.serialize(cmower));
}
}Rezultat programa je sledeći:
{"bookName":"Web Full Stack Development Advanced Road","writerName":"Chenmo Wang Er"}Iz rezultata se vidi:
Polje age klase Writer nema
@JsonFieldnapomenu, stoga nije serijalizovano.Polje name klase Writer ima
@JsonFieldnapomenu i eksplicitno je navedeno „writerName", pa je posle serijalizacije postalo writerName.Polje bookName klase Writer ima
@JsonFieldnapomenu, ali nije eksplicitno navedena vrednost, stoga je posle serijalizacije ostalo bookName.
„Kako ti se čini, Sanmej, nije baš teško?" upitam Sanmej.
„Napraviti napomenu zaista nije teško, ali taj JsonSerializer kasnije još uvek moram da probam da svladam." ozbiljno kaže Sanmej.
„Vredi, dobro ponovi, ja ću malo da pročitam „Principa kompilacije"."
„Hvala ti Erge, možeš malo da odmoriš."
„Da da."
Otvoreni kod na GitHubu sa više od 17000 zvezdica „Ergeov put do naprednog Jave“ konačno je izašao u PDF formatu prvog izdanja! Uključuje osnovnu sintaksu Jave, nizove & stringove, OOP, okvir kolekcija, Java IO, obradu izuzetaka, nove karakteristike Jave, mrežno programiranje, NIO, konkurentno programiranje, JVM i tako dalje, ukupno preko 320.000 reči, više od 500 ručno crtanih slika, može se reći da je jednostavno i razumevo, duhovito... Detalje: Sjajno, Java tutorijal na GitHubu sa više od 17000 zvezdica
