Olet todennäköisesti kohdannut tilanteita, joissa sinun täytyy olla tekemisissä metatiedot (data, joka kuvaa muita tietoja) luokkiin, menetelmiin ja / tai muihin sovelluselementteihin. Esimerkiksi ohjelmointitiimisi saattaa joutua tunnistamaan keskeneräiset luokat isossa sovelluksessa. Jokaisen keskeneräisen luokan metatietoihin sisältyisi todennäköisesti luokan viimeistelystä vastaavan kehittäjän nimi ja luokan odotettu valmistumispäivä.
Ennen Java 5: ää kommentit olivat ainoa joustava mekanismi, jonka Java oli tarjonnut metatietojen yhdistämiseksi sovelluselementteihin. Kommentit ovat kuitenkin huono valinta. Koska kääntäjä ohittaa ne, kommentit eivät ole käytettävissä ajon aikana. Ja vaikka niitä olisi saatavilla, teksti olisi jäsenneltävä tärkeiden tietoerien saamiseksi. Ilman standardoimista, miten tietoerät määritetään, näiden tietojen alkaminen voi osoittautua mahdottomaksi jäsentää.
lataa Hanki koodi Lataa lähdekoodi esimerkkejä tästä Java 101 -oppaasta. Tekijä: Jeff Friesen.Epätyypilliset merkintämekanismit
Java tarjoaa epätyypilliset mekanismit metatietojen liittämiseksi sovelluselementteihin. Esimerkiksi ohimenevä
varattu sana antaa sinun kommentoida (yhdistää tiedot) kenttiin, jotka on suljettava pois sarjoituksen aikana.
Java 5 muutti kaiken esittelemällä merkinnät, vakiomekanismi metatietojen liittämiseksi erilaisiin sovelluselementteihin. Tämä mekanismi koostuu neljästä osasta:
- An
@käyttöliittymä
mekanismi merkintätyyppien ilmoittamiseksi. - Sisällönmerkintätyypit, joiden avulla voit tunnistaa sovelluselementit, joihin merkintätyyppi liittyy; tunnistaa merkintä (merkintätyyppinen instanssi); ja enemmän.
- Tuki merkintöjen käsittelylle Java Reflection -sovellusliittymän laajennuksen kautta (josta keskustellaan tulevassa artikkelissa), jonka avulla voit löytää ohjelman ajonaikaiset merkinnät, ja yleistyneen työkalun merkintöjen käsittelyyn.
- Vakiomerkintätyypit.
Selitän kuinka käyttää näitä komponentteja työskennellessämme läpi tämän artikkelin.
Merkintätyyppien ilmoittaminen @interface
Voit ilmoittaa merkintätyypin määrittämällä @
-symboli, jota seuraa välittömästi käyttöliittymä
varattu sana ja tunniste. Esimerkiksi Listaus 1 ilmoittaa yksinkertaisen merkintätyypin, jota voit käyttää merkitsemään säikeetöntä koodia.
Listaus 1:ThreadSafe.java
public @interface ThreadSafe {}
Kun olet ilmoittanut tämän merkintätyypin, etuliitä menetelmät, joita pidät ketjun turvallisina, tämän tyyppisiin ilmentymiin valmistelemalla @
seuraa välittömästi tyypin nimi metodin otsikoihin. Listaus 2 tarjoaa yksinkertaisen esimerkin, jossa main ()
menetelmä on merkitty @ThreadSafe
.
Listaus 2:AnnDemo.java
(versio 1)
public class AnnDemo {@ThreadSafe public static void main (String [] argumentti) {}}
ThreadSafe
esiintymät eivät tarjoa muuta metatietoa kuin merkintätyypin nimi. Voit kuitenkin toimittaa metatietoja lisäämällä elementtejä tähän tyyppiin, jossa elementti on menetelmäotsikko, joka sijoitetaan merkintätyypin runkoon.
Sen lisäksi, että elementeillä ei ole koodirunkoja, niihin sovelletaan seuraavia rajoituksia:
- Metodin otsikko ei voi ilmoittaa parametreja.
- Metodin otsikko ei voi antaa heittolauseketta.
- Method-otsikon palautustyypin on oltava primitiivinen tyyppi (esim.
int
),java.lang.String
,java.lang.luokka
, luettelo, merkintätyyppi tai joukko näistä tyypeistä. Palautustyypille ei voida määrittää muuta tyyppiä.
Toisena esimerkkinä Listing 3 esittää a Tehdä
merkintätyyppi, jossa on kolme elementtiä, jotka tunnistavat tietyn koodaustyön, määrittelevät päivämäärän, jolloin työ on valmis, ja nimetään työn suorittamisesta vastaava kooderi.
Listaus 3:Tehtävä.java
(versio 1)
public @interface ToDo {int id (); Merkkijono viimeistelyPäiväys (); Merkkijonokooderi () oletus "ei ole"; }
Huomaa, että jokainen elementti ei ilmoita parametreja tai heittolausekkeita, sillä on laillinen palautustyyppi (int
tai Merkkijono
) ja päättyy puolipisteeseen. Viimeinen elementti paljastaa myös, että oletuspalautusarvo voidaan määrittää; tämä arvo palautetaan, kun merkinnällä ei määritetä arvoa elementille.
Luettelossa 4 käyttötarkoitusta Tehdä
merkitsemään keskeneräinen luokan menetelmä.
Listaus 4:AnnDemo.java
(versio 2)
julkinen luokka AnnDemo {public static void main (String [] args) {String [] kaupungit = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; lajitella (kaupungit); } @ToDo (id = 1000, finishDate = "10.10.2019", kooderi = "John Doe") staattinen void sort (Object [] objektit) {}}
Listaus 4 määrittää metadata-kohteen kullekin elementille; esimerkiksi, 1000
on osoitettu id
. Toisin kuin kooderi
, id
ja finishDate
elementit on määriteltävä; muuten kääntäjä ilmoittaa virheestä. Kun kooderi
sille ei ole määritetty arvoa, se olettaa oletusarvon "ei käytettävissä"
arvo.
Java tarjoaa erityisen Merkkijonoarvo ()
elementti, jota voidaan käyttää palauttamaan pilkulla erotettu metatieto-luettelo. Listaus 5 osoittaa tämän elementin uudistetussa versiossa Tehdä
.
Listaus 5:Tehtävä.java
(versio 2)
public @interface ToDo {Merkkijonoarvo (); }
Kun arvo()
on merkintätyypin ainoa elementti, sinun ei tarvitse määrittää sitä arvo
ja =
määritysoperaattori määritettäessä merkkijono tälle elementille. Listaus 6 osoittaa molemmat lähestymistavat.
Listaus 6:AnnDemo.java
(versio 3)
julkinen luokka AnnDemo {public static void main (String [] args) {String [] kaupungit = {"New York", "Melbourne", "Beijing", "Moscow", "Paris", "London"}; lajitella (kaupungit); } @ToDo (arvo = "1000,10 / 10/2019, John Doe") staattinen void sort (Object [] -objektit) {} @ToDo ("1000,10 / 10/2019, John Doe") staattinen looginen haku ( Object [] objektit, Object key) {return false; }}
Meta-merkintätyyppien käyttö - joustavuuden ongelma
Voit merkitä tyypit (esim. Luokat), menetelmät, paikalliset muuttujat ja paljon muuta. Tämä joustavuus voi kuitenkin olla ongelmallista. Voit esimerkiksi haluta rajoittaa Tehdä
vain menetelmiin, mutta mikään ei estä sitä käyttämästä muiden sovelluselementtien merkitsemistä, kuten Listing 7 osoittaa.
Listaus 7:AnnDemo.java
(versio 4)
@ToDo ("1000,10 / 10/2019, John Doe") julkisen luokan AnnDemo {public static void main (String [] args) {@ToDo (value = "1000,10 / 10/2019, John Doe") String [] kaupungit = {"New York", "Melbourne", "Beijing", "Moskova", "Pariisi", "Lontoo"}; lajitella (kaupungit); } @ToDo (arvo = "1000,10 / 10/2019, John Doe") staattinen void sort (Object [] -objektit) {} @ToDo ("1000,10 / 10/2019, John Doe") staattinen looginen haku ( Object [] objektit, Object key) {return false; }}
Luettelossa 7, Tehdä
käytetään myös merkitsemään AnnDemo
luokka ja kaupungeissa
paikallinen muuttuja. Näiden virheellisten merkintöjen läsnäolo saattaa hämmentää koodiasi tarkistavan henkilön tai jopa omat merkintöjen käsittelytyökalusi. Jos haluat pienentää merkintätyypin joustavuutta, Java tarjoaa Kohde
merkintätyyppi sen java.lang.merkintä
paketti.
Kohde
on meta-merkintätyyppi - merkintätyyppi, jonka merkinnät merkitsevät merkintätyyppejä, toisin kuin ei-meta-merkintätyyppi, jonka merkinnät merkitsevät sovelluselementtejä, kuten luokkia ja menetelmiä. Se tunnistaa sovelluksen elementtityypit, joihin merkintätyyppiä sovelletaan. Nämä elementit tunnistetaan Kohde
S ElementValue [] -arvo ()
elementti.
java.lang.annotation.ElementType
on enum, jonka vakiot kuvaavat sovelluselementtejä. Esimerkiksi, RAKENTAJA
koskee rakentajia ja PARAMETRI
koskee parametreja. Listing 8 refactors Listing 5's Tehdä
merkintätyyppi rajoittaa sen vain menetelmiin.
Listaus 8:Tehtävä.java
(versio 3)
tuo java.lang.annotation.ElementType; tuo java.lang.annotation.Target; @Target ({ElementType.METHOD}) public @interface ToDo {String value (); }
Ottaen huomioon uudistetun Tehdä
merkintätyyppi, yritys luoda luettelo 7 johtaa nyt seuraavaan virheilmoitukseen:
AnnDemo.java:1: virhe: merkintätyyppi ei koske tämän tyyppistä ilmoitusta @ToDo ("1000,10 / 10/2019, John Doe") ^ AnnDemo.java:6: virhe: merkintätyyppi ei koske tällaista ilmoitus @ToDo (arvo = "1000,10 / 10/2019, John Doe") ^ 2 virhettä
Muita meta-merkintätyyppejä
Java 5 esitteli kolme muuta meta-merkintätyyppiä, jotka löytyvät java.lang.merkintä
paketti:
Säilytys
ilmaisee kuinka kauan annotoidun tyyppisiä merkintöjä säilytetään. Tähän tyyppiin liittyyjava.lang.annotation.RetentionPolicy
enum julistaa vakiotLUOKKA
(kääntäjä tallentaa merkinnät luokkatiedostoon; virtuaalikone ei pidä niitä muistin säästämiseksi - oletuskäytäntö),RUNTIME
(kääntäjä tallentaa merkinnät luokkatiedostoon; virtuaalikone säilyttää ne), jaLÄHDE
(kääntäjä hylkää merkinnät).Dokumentoitu
osoittaa, ettäDokumentoitu
- merkitsemättömät merkinnät on dokumentoitavajavadoc
ja vastaavia työkaluja.Peritty
ilmaisee, että merkintätyyppi peritään automaattisesti.
Java 8 esitteli java.lang.annotation.Muistettavissa
meta-merkintätyyppi. Toistettavissa
käytetään osoittamaan, että merkintätyyppi, jonka ilmoituksen se (meta-) merkitsee, on toistettavissa. Toisin sanoen voit soveltaa useita merkintöjä samasta toistettavasta merkintätyypistä sovelluselementtiin, kuten tässä osoitetaan:
@ToDo (arvo = "1000,10 / 10/2019, John Doe") @ToDo (arvo = "1001,10 / 10/2019, Kate Doe") staattinen void sort (Object [] -objektit) {}
Tässä esimerkissä oletetaan, että Tehdä
on merkitty Toistettavissa
merkinnän tyyppi.
Käsitellään merkintöjä
Merkinnät on tarkoitettu käsiteltäviksi; muuten ei ole mitään järkeä pitää niitä. Java 5 laajensi Reflection-sovellusliittymää auttamaan sinua luomaan omia merkintöjen käsittelytyökaluja. Esimerkiksi, Luokka
julistaa Merkintä [] getAnnotations ()
menetelmä, joka palauttaa taulukon java.lang. huomautus
esimerkit, jotka kuvaavat merkintöjä, jotka esiintyvät Luokka
esine.
Listaus 9 esittää yksinkertaisen sovelluksen, joka lataa luokkatiedoston, kyselee sen menetelmiä Tehdä
merkinnät ja tuottaa kunkin löydetyn merkinnän komponentit.
Listaus 9:AnnProcDemo.java
tuo java.lang.reflect.Method; public class AnnProcDemo {public static void main (String [] args) heittää poikkeuksen {if (args.pituus! = 1) {System.err.println ("käyttö: java AnnProcDemo classfile"); palata; } Method [] metodit = Class.forName (argumentit [0]). GetMethods (); for (int i = 0; i <method.length; i ++) {if (method [i] .isAnnotationPresent (ToDo.class)) {ToDo todo = metodit [i] .getAnnotation (ToDo.class); Merkkijono [] komponentit = todellinen.arvo (). Split (","); System.out.printf ("ID =% s% n", komponentit [0]); System.out.printf ("Viimeinen päivä =% s% n", komponentit [1]); System.out.printf ("kooderi =% s% n% n", komponentit [2]); }}}}
Kun olet varmistanut, että täsmälleen yksi komentoriviargumentti (luokkatiedoston tunnistaminen) on määritetty, main ()
lataa luokkatiedoston kautta Class.forName ()
, vetoaa getMethods ()
palauttaa joukko java.lang.reflect.Method
kaikki tunnistavat objektit julkinen
menetelmät luokkatiedostossa ja käsittelee nämä menetelmät.
Menetelmän käsittely alkaa vetoamalla Menetelmä
S looginen isAnnotationPresent (luokan annotationClass)
menetelmä sen määrittämiseksi, onko Tehtävä.luokka
on läsnä menetelmässä. Jos niin, Menetelmä
S T getAnnotation (luokan annotationClass)
menetelmää kutsutaan merkinnän saamiseksi.
Tehdä
Käsiteltyjä merkintöjä ovat ne, joiden tyypit ilmoittavat yhden Merkkijonoarvo ()
elementti (katso luettelo 5). Koska tämän elementin merkkijonopohjaiset metatiedot on erotettu pilkuilla, se on jaettava komponenttiarvojen ryhmäksi. Kumpaankin kolmesta komponenttiarvosta pääsee sitten käsiksi ja tulostetaan.
Käännä tämä lähdekoodi (javac AnnProcDemo.java
). Ennen kuin voit käyttää sovellusta, tarvitset sopivan luokkatiedoston @Tehdä
sen merkinnät julkinen
menetelmiä. Voit esimerkiksi muokata luetteloa 6 AnnDemo
lisättävä lähdekoodi julkinen
sen järjestellä()
ja Hae()
menetelmän otsikot. Tarvitset myös Listaus 10: n Tehdä
merkintätyyppi, joka vaatii RUNTIME
säilyttämispolitiikka.
Listaus 10:Tehtävä.java
(versio 4)
tuo java.lang.annotation.ElementType; tuo java.lang.annotation.Retention; tuo java.lang.annotation.RetentionPolicy; tuo java.lang.annotation.Target; @Target ({ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) public @interface ToDo {String value (); }
Koosta muokattu AnnDemo.java
ja Listaus 10, ja suorita seuraava komento käsiteltäväksi AnnDemo
S Tehdä
merkinnät:
java AnnProcDemo AnnDemo
Jos kaikki menee hyvin, sinun on noudatettava seuraavaa tulosta:
ID = 1000 Viimeistelypäivä = 10.10.2019 Coder = John Doe ID = 1000 Viimeistelypäivä = 10.10.2019 Coder = John Doe
Käsitellään merkintöjä apt: llä ja Java-kääntäjällä
Java 5 esitteli apt
työkalu merkintöjen yleiseen käsittelyyn. Java 6 siirtyi apt
Toiminnallisuutta javac
kääntäjä ja Java 7 vanhentuneet apt
, joka myöhemmin poistettiin (alkaen Java 8: sta).
Vakiomerkintätyypit
Kera Kohde
, Säilytys
, Dokumentoitu
ja Peritty
, Java 5 esiteltiin java.lang. vanhentunut
, java.lang.Ohita
ja java.lang.SuppressWarnings
. Nämä kolme merkintätyyppiä on suunniteltu käytettäväksi vain kääntäjän yhteydessä, minkä vuoksi niiden säilytyskäytännöiksi on asetettu LÄHDE
.