Liigu peamise sisu juurde

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.

nõuanne

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 ainult public elemente, 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

AlguspunktKuidas saada Class objekt
Objekti eksemplarobj.getClass()
Kompileerimise ajal teada tüüpMyType.class
Klassinimi sõnena käitamise ajalClass.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.