Apa Reflection API kanggo?

Mekanisme refleksi Jawa ngidini pangembang nggawe owah-owahan lan entuk informasi babagan kelas, antarmuka, kolom, lan metode nalika runtime tanpa ngerti jenenge.

API Refleksi uga ngidini sampeyan nggawe obyek anyar, cara nelpon, lan entuk utawa nyetel nilai lapangan.

Ayo nggawe dhaptar kabeh sing bisa ditindakake kanthi nggunakake refleksi:

  • Ngenali / nemtokake kelas obyek
  • Entuk informasi babagan modifiers kelas, kolom, metode, konstanta, konstruktor, lan superclass
  • Temokake metode apa sing kalebu antarmuka sing diimplementasikake
  • Nggawe conto kelas sing jeneng kelas ora dikenal nganti program dieksekusi
  • Entuk lan setel nilai lapangan conto kanthi jeneng
  • Nelpon cara conto kanthi jeneng

Meh kabeh teknologi Jawa modern nggunakake refleksi. Iki ndasari umume kerangka lan perpustakaan Java / Java EE saiki, contone:

  • Spring frameworks kanggo mbangun aplikasi web
  • kerangka testing JUnit

Yen sampeyan durung nate nemoni mekanisme kasebut sadurunge, sampeyan bisa uga takon kenapa kabeh iki perlu. Jawaban iki cukup prasaja nanging uga ora jelas: refleksi kanthi dramatis nambah keluwesan lan kemampuan kanggo ngatur aplikasi lan kode kita.

Nanging tansah ana pro lan cons. Dadi ayo sebutno sawetara kontra:

  • Pelanggaran keamanan aplikasi. Refleksi ngidini kita ngakses kode sing ora kudu (nglanggar enkapsulasi).
  • Watesan keamanan. Refleksi mbutuhake ijin runtime sing ora kasedhiya kanggo sistem sing mbukak manajer keamanan.
  • kinerja kurang. Refleksi ing Jawa nemtokake jinis kanthi dinamis kanthi mindhai classpath kanggo nemokake kelas sing bakal dimuat. Iki nyuda kinerja program.
  • angel kanggo njaga. Kode sing nggunakake refleksi angel diwaca lan debug. Iku kurang fleksibel lan harder kanggo njaga.

Nggarap kelas nggunakake API Refleksi

Kabeh operasi refleksi diwiwiti kanthi obyek java.lang.Class . Kanggo saben jinis obyek, conto immutable saka java.lang.Class digawe. Iki nyedhiyakake cara kanggo njupuk properti obyek, nggawe obyek anyar, lan cara nelpon.

Ayo goleki dhaptar metode dhasar kanggo nggarap java.lang.Class :

Metode Tumindak
String getName(); Ngasilake jeneng kelas
int getModifiers(); Ngasilake modifiers akses
Paket getPackage(); Ngasilake informasi babagan paket
Kelas getSuperclass(); Ngasilake informasi babagan kelas induk
Kelas [] getInterfaces(); Ngasilake macem-macem antarmuka
Konstruktor[] getConstructors(); Ngasilake informasi babagan konstruktor kelas
Fields[] getFields(); Ngasilake lapangan kelas
Field getField(String fieldName); Ngasilake lapangan tartamtu saka kelas kanthi jeneng
Metode[] getMethods(); Ngasilake macem-macem cara

Iki minangka cara sing paling penting kanggo njupuk data babagan kelas, antarmuka, kolom, lan metode. Ana uga cara sing ngidini sampeyan entuk utawa nyetel nilai lapangan, lan ngakses kolom pribadi . Kita bakal ndeleng wong-wong mau mengko.

Saiki kita bakal ngomong babagan njupuk java.lang.Class dhewe. Kita duwe telung cara kanggo nindakake iki.

1. Nggunakake Class.forName

Ing aplikasi sing mlaku, sampeyan kudu nggunakake metode forName(String className) kanggo entuk kelas.

Kode iki nduduhake carane kita bisa nggawe kelas nggunakake bayangan. Ayo nggawe kelas Person sing bisa digarap:

package com.company;

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Lan bagean kapindho conto kita yaiku kode sing nggunakake refleksi:

public class TestReflection {
    public static void main(String[] args) {
        try {
            Class<?> aClass = Class.forName("com.company.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Pendekatan iki bisa ditindakake yen jeneng lengkap kelas kasebut dikenal. Banjur sampeyan bisa entuk kelas sing cocog nggunakake metode Class.forName () statis . Cara iki ora bisa digunakake kanggo jinis primitif.

2. Nggunakake .class

Yen jinis kasedhiya nanging ora ana conto, sampeyan bisa entuk kelas kanthi nambahake .class menyang jeneng jinis. Iki minangka cara paling gampang kanggo entuk kelas jinis primitif.

Class aClass = Person.class;

3. Nggunakake .getClass()

Yen obyek kasedhiya, banjur cara paling gampang kanggo njaluk kelas nelpon object.getClass () .

Person person = new Person();
Class aClass = person.getClass();

Apa bedane antarane rong pendekatan pungkasan?

Gunakake A.class yen sampeyan ngerti obyek kelas sing kasengsem ing wektu coding. Yen ora ana conto sing kasedhiya, sampeyan kudu nggunakake .class .

Njupuk metode kelas

Ayo goleki cara sing ngasilake metode kelas kita: getDeclaredMethods () lan getMethods () .

getDeclaredMethods () ngasilake array sing ngemot obyek Metode kanggo kabeh metode sing diumumake saka kelas utawa antarmuka sing diwakili dening obyek Kelas, kalebu metode umum, pribadi, standar, lan sing dilindhungi, nanging metode sing ora diwarisake.

getMethods () ngasilake array sing ngemot obyek Metode kanggo kabeh metode umum saka kelas utawa antarmuka sing diwakili dening obyek Kelas - sing diumumake dening kelas utawa antarmuka, uga sing diwarisake saka superclasses lan superinterfaces.

Ayo goleki carane saben wong bisa dianggo.

Ayo dadi miwiti karo getDeclaredMethods() . Kanggo maneh bantuan kita ngerti prabédan antarane rong cara, ing ngisor iki kita bakal bisa karo kelas Nomer abstrak . Ayo nulis metode statis sing bakal ngowahi array Metode kita dadi List<String> :

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Method[] declaredMethods = Number.class.getDeclaredMethods();
        List<String> actualMethodNames = getMethodNames(declaredMethods);
        actualMethodNames.forEach(System.out::println);
    }

    private static List<String> getMethodNames(Method[] methods) {
        return Arrays.stream(methods)
                .map(Method::getName)
                .collect(Collectors.toList());
    }
}

Iki minangka asil nglakokake kode iki:

byteValue
shortValue
intValue
longValue
float floatValue;
doubleValue

Iki minangka cara sing diumumake ing kelas Nomer . Apa getMethods() bali? Ayo ngganti rong baris ing conto:

final Method[] methods = Number.class.getMethods();
List<String> actualMethodNames = getMethodNames(methods);

Kanthi nindakake iki, kita bakal weruh set metode ing ngisor iki:

byteValue
shortValue
intValue
longValue
float floatValue;
doubleValue
wait
wait
wait
padha
karoString
hashCode
getClass
notify
notifyAll

Amarga kabeh kelas marisi Obyek , metode kita uga ngasilake metode umum saka kelas Obyek .

Njupuk lapangan saka kelas

Metode getFields lan getDeclaredFields digunakake kanggo njupuk kolom kelas. Minangka conto, ayo goleki kelas LocalDateTime . Kita bakal nulis maneh kode kita:

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class TestReflection {
    public static void main(String[] args) {
        final Field[] declaredFields = LocalDateTime.class.getDeclaredFields();
        List<String> actualFieldNames = getFieldNames(declaredFields);
        actualFieldNames.forEach(System.out::println);
    }

    private static List<String> getFieldNames(Field[] fields) {
        return Arrays.stream(fields)
                .map(Field::getName)
                .collect(Collectors.toList());
    }
}

Minangka asil ngeksekusi kode iki, kita entuk set lapangan sing ana ing kelas LocalDateTime.

MIN
MAX
serialVersionUID wektu
tanggal

Kanthi analogi karo metode pemeriksaan sadurunge, ayo ndeleng apa sing kedadeyan yen kita ngganti kode kasebut:

final Field[] fields = LocalDateTime.class.getFields();
List<String> actualFieldNames = getFieldNames(fields);

Output:

MIN
MAX

Saiki ayo goleki bedane antarane metode kasebut.

Metode getDeclaredFields ngasilake array obyek Field kanggo kabeh kolom sing diumumake dening kelas utawa antarmuka sing diwakili dening ikikelasobyek.

Metode getFields ngasilake array obyek Field kanggo kabeh lapangan umum saka kelas utawa antarmuka sing diwakili deningkelasobyek.

Saiki ayo goleki nang LocalDateTime .

KelaseMINlanMAXlapangan umum, tegese bakal katon liwat metode getFields . Miturut kontras, ingtanggal,wektu,serialVersionUIDcara duwe modifier pribadi , kang tegese padha ora bakal katon liwat cara getFields , nanging kita bisa njaluk wong nggunakake getDeclaredFields . Iki carane kita bisa ngakses obyek Field kanggo kothak pribadi .

Katrangan cara liya

Saiki wektune kanggo ngomong babagan sawetara metode kelas Kelas , yaiku:

Metode Tumindak
getModifiers Njupuk modifiers kanggo kelas kita
getPaket Njupuk paket sing ngemot kelas kita
njalukSuperclass Njupuk kelas induk
getInterfaces Entuk macem-macem antarmuka sing ditindakake dening kelas
njalukName Entuk jeneng kelas sing mumpuni
getSimpleName Njupuk jeneng kelas

getModifiers()

Modifiers bisa diakses nggunakake akelasobyek.

Modifiers minangka tembung kunci kaya public , static , interface , etc. Kita entuk modifiers nggunakake metode getModifiers() :

Class<Person> personClass = Person.class;
int classModifiers = personClass.getModifiers();

Kode iki nyetel nilai aintvariabel sing rada lapangan. Saben modifier akses bisa diuripake utawa dipateni kanthi nyetel utawa mbusak bit sing cocog. Kita bisa mriksa modifiers nggunakake cara ing java.lang.reflect.Modifier kelas:

import com.company.Person;
import java.lang.reflect.Modifier;

public class TestReflection {
    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        int classModifiers = personClass.getModifiers();

        boolean isPublic = Modifier.isPublic(classModifiers);
        boolean isStatic = Modifier.isStatic(classModifiers);
        boolean isFinal = Modifier.isFinal(classModifiers);
        boolean isAbstract = Modifier.isAbstract(classModifiers);
        boolean isInterface = Modifier.isInterface(classModifiers);

        System.out.printf("Class modifiers: %d%n", classModifiers);
        System.out.printf("Is public: %b%n", isPublic);
        System.out.printf("Is static: %b%n", isStatic);
        System.out.printf("Is final: %b%n", isFinal);
        System.out.printf("Is abstract: %b%n", isAbstract);
        System.out.printf("Is interface: %b%n", isInterface);
    }
}

Elinga kaya apa deklarasi Pribadi kita:

public class Person {}

Kita entuk output ing ngisor iki:

Modifiers kelas: 1
Umum: bener
Statis: palsu
final: palsu
abstrak: palsu
antarmuka: palsu

Yen kita nggawe kelas abstrak, mula kita duwe:

public abstract class Person {}

lan output iki:

Modifiers kelas: 1025
Umum: bener
Iku statis: palsu
Iku final: palsu
Iku abstrak: bener
Iku antarmuka: palsu

Kita ngganti modifier akses, tegese kita uga ngganti data sing bali liwat metode statis kelas Modifier .

getPaket()

Mung ngerti kelas, kita bisa entuk informasi babagan paket:

Class<Person> personClass = Person.class;
final Package aPackage = personClass.getPackage();
System.out.println(aPackage.getName());

getSuperclass()

Yen kita duwe obyek Kelas, kita bisa ngakses kelas induk:

public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<? super Person> superclass = personClass.getSuperclass();
    System.out.println(superclass);
}

Kita entuk kelas Obyek sing kondhang :

class java.lang.Object

Nanging yen kelas kita duwe kelas induk liyane, mula kita bakal weruh:

package com.company;

class Human {
    // Some info
}

public class Person extends Human {
    private int age;
    private String name;

    // Some info
}

Ing kene kita entuk kelas induk:

class com.company.Human

getInterfaces()

Mangkene carane kita bisa entuk dhaptar antarmuka sing ditindakake dening kelas:

public static void main(String[] args) {
    Class<Person> personClass = Person.class;
    final Class<?>[] interfaces = personClass.getInterfaces();
    System.out.println(Arrays.toString(interfaces));
}

Lan aja lali ngganti kelas Person :

public class Person implements Serializable {}

Output:

[interface java.io.Serializable]

Kelas bisa ngleksanakake akeh antarmuka. Mulane kita njaluk Uploaded sakakelasobyek. Ing Java Reflection API, antarmuka uga diwakili deningkelasobyek.

Wigati dimangerteni: Cara kasebut mung ngasilake antarmuka sing ditindakake dening kelas sing ditemtokake, dudu kelas induk. Kanggo njaluk dhaftar lengkap antarmuka dipun ginakaken dening kelas, sampeyan kudu ngrujuk loro kelas saiki lan kabeh leluhur munggah chain pusaka.

getName() & getSimpleName() & getCanonicalName()

Ayo nulis conto sing kalebu primitif, kelas bersarang, kelas anonim, lan kelas String :

public class TestReflection {
    public static void main(String[] args) {
        printNamesForClass(int.class, "int class (primitive)");
        printNamesForClass(String.class, "String.class (ordinary class)");
        printNamesForClass(java.util.HashMap.SimpleEntry.class,
                "java.util.HashMap.SimpleEntry.class (nested class)");
        printNamesForClass(new java.io.Serializable() {
                }.getClass(),
                "new java.io.Serializable(){}.getClass() (anonymous inner class)");
    }

    private static void printNamesForClass(final Class<?> clazz, final String label) {
        System.out.printf("%s:%n", label);
        System.out.printf("\tgetName()):\t%s%n", clazz.getName());
        System.out.printf("\tgetCanonicalName()):\t%s%n", clazz.getCanonicalName());
        System.out.printf("\tgetSimpleName()):\t%s%n", clazz.getSimpleName());
        System.out.printf("\tgetTypeName():\t%s%n%n", clazz.getTypeName());
    }
}

Hasil program kami:

kelas int (primitif):
getName()): int
getCanonicalName()): int
getSimpleName()): int
getTypeName(): int

String.class (kelas biasa):
getName()): java.lang.String
getCanonicalName() ): java.lang.String
getSimpleName()): String
getTypeName(): java.lang.String

java.util.HashMap.SimpleEntry.class (nested class):
getName()): java.util.AbstractMap$SimpleEntry
getCanonicalName( )): java.util.AbstractMap.SimpleEntry
getSimpleName()): SimpleEntry
getTypeName(): java.util.AbstractMap$SimpleEntry

new java.io.Serializable(){}.getClass() (kelas batin anonim):
getName() ): TestReflection $1
getCanonicalName()): null
getSimpleName()):
getTypeName(): TestReflection $1

Saiki ayo analisa output program kita:

  • getName () ngasilake jeneng entitas.

  • getCanonicalName() ngasilake jeneng kanonik kelas dasar, kaya sing ditegesake dening Spesifikasi Basa Jawa. Ngasilake null yen kelas dhasar ora duwe jeneng kanonik (yaiku, yen kelas lokal utawa anonim utawa array sing jinis unsur ora duwe jeneng kanonik).

  • getSimpleName () ngasilake jeneng prasaja saka kelas dhasar minangka kasebut ing kode sumber. Ngasilake string kosong yen kelas dhasar anonim.

  • getTypeName () ngasilake senar informatif kanggo jeneng jinis iki.