Class-objekt
Sissejuhatus
Iga tüüp, mis on JVM-i poolt mällu laetud, on käitamise ajal esindatud Class<?>-tüüpi objektina.
See objekt on reflektsiooni sisenemispunkt: selle kaudu saab küsida tüübi nime, selle välju, meetodeid, annotatsioone, ülemklassi ja palju muud.
Enne reflektsiooniga millegi tegemist tuleb kõigepealt saada kätte õige Class<?> objekt.
Selleks on kolm levinud viisi, sõltuvalt sellest, mis on ette antud.
Kolm viisi Class-objekti saamiseks
Instantsist
Objektist on võimalik käitusaegne tüüp kätte saada järgnevalt:
Book book = new Book("Clean Code", "Robert C. Martin", 2008);
Class<?> type = book.getClass();
System.out.println(type.getName()); // com.example.library.Book
System.out.println(type.getSimpleName()); // Book
getClass() tagastab objekti tegeliku käitusaegse tüübi, mitte muutuja deklareeritud tüübi.
Kui book oleks salvestatud muutujasse tüübiga Object, tagastaks book.getClass() ikkagi Book.class-i.
Klassiliteraalist
Kui tüüp on kompileerimise ajal teada, siis kõige puhtam viis on kasutada klassiliteraali (class literal):
Class<Book> type = Book.class;
See töötab ka primitiivtüüpide, massiivide ja liidestega:
Class<?> intType = int.class;
Class<?> stringArrayType = String[].class;
Class<?> listType = List.class;
Klassiliteraali kontrollitakse kompileerimise ajal - kui Book on valesti kirjutatud,
siis kood ei kompileeru.
Sõnest käitusajal
Kui klassi nimi on teada alles käitamise ajal - näiteks kuna see tuleb konfiguratsioonifailist või kasutaja sisendist - kasutatakse Class.forName meetodit:
Class<?> type = Class.forName("com.example.library.Book");
forName võtab argumendiks klassi täielikult kvalifitseeritud nime (paki nimi koos klassi nimega) ning viskab ClassNotFoundException erindi, kui sellist klassi ei leidu.
Enamik raamistikke tugineb just selle meetodi peale, kuna need loevad tüüpide nimed sisse välistest allikatest ega tea neid ette.
Erinevalt eelnevatest on forName ainus, mis võib käitusajal ebaõnnestuda.
Sellega tegutsedes peab alati ClassNotFoundException-i käsitlema.
Mida Class-objekt võimaldab
Class<?> objekt pakub hulganisti meetodeid, mis võimaldavad klassi kohta infot küsida:
Class<?> type = Book.class;
type.getName(); // "com.example.library.Book"
type.getSimpleName(); // "Book"
type.getPackageName(); // "com.example.library"
type.getSuperclass(); // Class object for the parent class
type.getInterfaces(); // Class[] of implemented interfaces
type.getDeclaredFields(); // Field[] - fields declared in this class
type.getDeclaredMethods(); // Method[] - methods declared in this class
type.getDeclaredConstructors(); // Constructor[]
type.getAnnotations(); // Annotation[]
Järgmised peatükid katavad neist enim kasutatavad põhjalikumalt.
Samuti pange läbivalt tähele, mis erinevus on getXxx ja getDeclaredXxx meetoditel:
getFields(),getMethods()- need tagastavad ainultpublicelemente, mis klassil on (sealhulgas ka neid, mida ülemklassilt päritakse)getDeclaredFields(),getDeclaredMethods()- need tagastavad kõik elemendid, mis klassil on (ka privaatsed), kuid piirdutakse antud klassi raames. Pärituid elemente ei kuvata siin.
Enamus tegevuste jaoks peaksite kasutama getDeclaredXxx variante, kuna enamus kordadest huvitab meid info klassi enda kohta, mitte ainult avaliku liidese kohta.
Tüübisuhete kontrollimine
Kahte class objekti vahel saab kontrollida alamtüübi seoseid.
a.isAssignableFrom(b) tagastab true, kui b eksemplari saab omistada a-tüüpi muutujale.
See tähendab, et b on sama klass mis a, selle alamklass või seda realiseeriv klass:
Number.class.isAssignableFrom(Integer.class); // true
Integer.class.isAssignableFrom(Number.class); // false
List.class.isAssignableFrom(ArrayList.class); // true
isInstance(obj) on instanceof-i reflektiivne vaste:
Number.class.isInstance(42); // true
Number.class.isInstance("hello"); // false
Need on kasulikud siis, kui võrreldavad tüübid on teada alles programmi käitamise ajal, näiteks kontrollimaks, kas visatud erind on oodatud tüüpi, või kas meetodi tagastatud väärtus vastab sellele, mida väljakutsuja ootas.
Class-objektide võrdsus
Iga klassi kohta eksisteerib ühe klassilaaduri (classloader) kohta täpselt üks Class objekt, seega saab kahte Class viidet võrrelda == operaatoriga:
book.getClass() == Book.class; // true
Class.forName("com.example.library.Book") == Book.class; // true
Sellest tulenevalt puudub vajadus kasutada .equals()-it, kui kontrollitakse, kas kas viidet kirjeldavad sama tüüpi.
Lühikokkuvõte
| Alguspunkt | Kuidas saada Class objekt |
|---|---|
| Objekti eksemplar | obj.getClass() |
| Kompileerimise ajal teada tüüp | MyType.class |
| Klassinimi sõnena käitamise ajal | Class.forName("...") |
Kui Class<?> objekt on olemas, siis on kogu ülejäänud reflektsiooni API selle kaudu
kättesaadav: väljad, meetodid, konstruktorid ja annotatsioonid on kõik sellega seotud.