java.lang.reflect.Field class

Kelas Field menyediakan maklumat tentang dan akses dinamik kepada satu medan kelas atau antara muka. Medan juga membenarkan penukaran jenis melebar semasa operasi dapatkan atau tetapkan akses, tetapi melontarkan IllegalArgumentException jika penyempitan akan berlaku.

Untuk mendapatkan objek Field , kami akan menulis kelas dahulu:


public class Person {
    private String name;
    private int age;
    
    public boolean isMale;
    
    protected String address;
    
    public static final int MAX_AGE = 120;
    public static final int MIN_AGE = 0;
}

Dan inilah kod kami untuk bekerja dengan kelas itu:


public class Main {
    public static void main(String[] args) {
        Field[] fields = Person.class.getDeclaredFields();
        List<Field> actualFields = getFieldNames(fields);
        System.out.println(actualFields);
    }

    static List<Field> getFieldNames(Field[] fields) {
        return List.of(fields);
    }
}

Ini adalah cara kami mendapatkan senarai medan kelas kami, yang akan kami kerjakan kemudian. Inilah hasilnya:

[java.lang.String peribadi com.company.Person.name, private int com.company.Person.age, public boolean com.company.Person.isMale, protected java.lang.String com.company.Person.address, public static final int com.company.Person.MAX_AGE, public static final int com.company.Person.MIN_AGE]

Sekarang mari kita fikirkan apa yang boleh kita lakukan dengan data ini. Mari kita bincangkan tentang kaedah kelas Field :

Kaedah Penerangan
getType() Mengembalikan objek Kelas yang mengenal pasti jenis medan yang diisytiharkan yang diwakili oleh iniPadangobjek.
getAnnotatedType() Mengembalikan anJenis Beranotasiobjek yang mewakili penggunaan jenis untuk menentukan jenis medan yang diisytiharkan yang diwakili oleh Medan ini.
getGenericType() Pulangan ataipobjek yang mewakili jenis medan yang diisytiharkan yang diwakili oleh iniPadangobjek.
getName() Mengembalikan nama medan yang diwakili oleh iniPadangobjek.
getModifiers() Mengembalikan pengekodan int pengubah bahasa Java untuk medan yang diwakili oleh iniPadangobjek.
getAnnotations() Mengembalikan anotasi untuk medan ini. Jika tiada anotasi, ia mengembalikan tatasusunan kosong.

kaedah getType(), getName(), dan getModifiers().

Kita boleh menggunakan kaedah getType() untuk mendapatkan jenis medan kita. Mari tulis kaedah:


static void printTypes(List<Field> fields){
      fields.forEach(e -> System.out.println(e.getType()));
  }

Dan kami mendapat keputusan ini:

class java.lang.String
int
boolean
class java.lang.String
int
int

Sekarang mari kita tambahkan kaedah pada kelas kita untuk mendapatkan nama medan. Ini akan memudahkan anda menavigasi medan kelas kami.


static void printTypesAndNames(List<Field> fields){
   fields.forEach(e -> System.out.printf("Field type - %s\nField name - %s\n\n", e.getType(), e.getName()));
}

Sekarang hasilnya lebih mudah difahami:

Jenis medan - kelas java.lang.String
Nama medan - nama

Jenis medan - int
Nama medan - umur

Jenis medan - boolean
Nama medan - isMale

Jenis medan - kelas java.lang.String
Nama medan - alamat

Jenis medan - int
Nama medan - MAX_AGE

Jenis medan -
nama medan int - MIN_AGE

Hebat! Mari ubah suai kaedah kami lagi! Kami akan menambah pengubah suai akses di sini


static void printFieldInfo(List<Field> fields){
   fields.forEach(e -> System.out.printf("Field type - %s\nField name - %s\nModifiers - %s\n\n", e.getType(), e.getName(), Modifier.toString(e.getModifiers())));
}

Dan mari kita lihat apa yang e.getModifiers() pulangkan. Kaedah ini mengembalikan int yang membolehkan kami menentukan keadaan pengubah akses medan kami. Kelas Modifier mengandungi pembolehubah statik yang bertanggungjawab untuk setiap pengubah khusus medan.

Mari kita bungkus nilai pulangan kita dalam Modifier.toString() dan segera dapatkan nilainya sebagai teks:

Jenis medan - kelas java.lang.String
Nama medan - nama Pengubah suai - jenis Medan
peribadi - int Nama medan - umur Pengubah suai - jenis Medan peribadi - boolean Nama Medan - isMale Modifiers - jenis Medan awam - kelas java.lang.String Nama Medan - alamat Pengubah suai - jenis Medan dilindungi - int Nama medan - MAX_AGE Pengubah suai - akhir statik awam Jenis medan - nama medan int - MIN_AGE Pengubah suai - akhir statik awam



















Inilah rupanya tanpa Modifier.toString() :

Jenis medan - kelas java.lang.String
Nama medan - nama
Pengubah suai - 2

Jenis medan - int
Nama medan - umur
Pengubah suai - 2

Jenis medan - boolean
Nama medan - isMale
Pengubah suai - 1

Jenis medan - kelas java.lang.String
Nama medan - alamat
Pengubahsuai - 4

Jenis medan - int
Nama medan - MAX_AGE
Pengubah suai - 25

Jenis medan - int
Nama medan - MIN_AGE
Pengubah suai - 25

kaedah getAnnotations(), getAnnotatedType(), dan getGenericType().

Mari kita ubah suai kelas Orang untuk bekerja dengan kaedah ini. Kami akan menulis anotasi kami sendiri dan menerapkannya pada medan kami. Dan kami akan menambah beberapa lagi medan.

Mari buat dua anotasi. Kami akan menghantar nama pembolehubah dalam Pig Latin kepada satu, dan kami akan menggunakan yang kedua untuk elemen:


@Target(value=ElementType.FIELD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Name {
    String name();
}

@Target({ ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Number {
}

Dan kami akan menukar kelas utama kami dan kelas Person :


public class Person {
    @Name(name = "Ame-nay")
    private String name;

    @Name(name = "User nicknames")
    List<String> nicknames;

    private final Class<Object> type;

    private int @Number[] number;

    public Person(Class<Object> type) {
        this.type = type;
    }
}

public static void main(String[] args) {
    Field[] fields = Person.class.getDeclaredFields();
    List<Field> actualFields = getFieldNames(fields);
    
    printAdditionalInfo(actualFields);
}

static void printAdditionalInfo(List<Field> fields) {
   System.out.println("\ngetAnnotatedType:");
   fields.forEach(e -> System.out.println(e.getAnnotatedType()));

   System.out.println("\ngetGenericType:");
   fields.forEach(e -> System.out.println(e.getGenericType()));

   System.out.println("\ngetAnnotations:");
   fields.forEach(e -> System.out.println(Arrays.toString(e.getAnnotations())));
}

Sudah tiba masanya untuk melihat hasil kaedah kami dan mengetahui untuk kegunaannya:

getAnnotatedType:
java.lang.Class<java.lang.Object>
java.util.List<java.lang.String>
java.lang.String
int @Number()[]

getGenericType:
java.lang.Class<java.lang. Objek>
java.util.List<java.lang.String>
kelas java.lang.String
kelas [Saya

mendapatAnnotations:
[]
[@Name(name="\u0055\u0073\u0065\u0072\u0020\u006e\u0069\u0063 \u006b\u006e\u0061\u006d\u0065\u0073")]
[@Nama(nama="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [
]
  • getAnnotatedType mengembalikan anotasi untuk medan yang diberikan, jika ada. Kami mendapat anotasi untuk medan dan kami dapat melihatnya dengan sempurna.

  • getGenericType membolehkan anda memaparkan jenis parameter dengan betul.

  • getAnnotations mengembalikan anotasi yang digunakan pada objek kami.

Beginilah cara kami boleh mendapatkan semua data tentang setiap medan dalam kelas kami dengan mudah, serta pengubah suai akses, anotasi dan jenis datanya.

java.lang.reflect.Kaedah kelas

Super! Kami telah bercakap tentang bidang kelas kami. Kini tiba masanya untuk bercakap tentang kaedah.

Untuk mendapatkan objek Kaedah , kami memanggil kaedah getMethod , memberikannya nama kaedah kami. Ini adalah cara asas untuk mendapatkan objek Kaedah :


Method getNameMethod =  Person.class.getMethod("getName");

Kami akan terus bekerja dengan kelas kami. Mari tambahkan getter dan setter, dan kaedah hashCode, equals dan toString :


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

    public boolean isMale;

    protected String address;

    public static final int MAX_AGE = 120;
    public static final int MIN_AGE = 0;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", isMale=" + isMale +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && isMale == person.isMale && Objects.equals(name, person.name) && Objects.equals(address, person.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, isMale, address);
    }
}

Sekarang mari kita sediakan satu set kaedah yang akan kita periksa menggunakan kelas Kaedah . Berikut ialah senarai kaedah yang paling penting:

Kaedah Penerangan
getName() Mengembalikan nama kaedah.
getModifiers() Mengembalikan pengubah suai akses kaedah ini.
getReturnType() Mengembalikan jenis pemulangan kaedah.
getGenericReturnType() Mengembalikan jenis pemulangan kaedah, mengambil kira kaedah generik.
getParameterTypes() Mengembalikan tatasusunan parameter kaedah.
getGenericParameterTypes() Mengembalikan tatasusunan parameter kaedah, mengambil kira kaedah generik.
getExceptionTypes() Mengembalikan pengecualian yang boleh dibuang oleh kaedah.
getGenericExceptionTypes() Mengembalikan pengecualian yang boleh dibuang oleh kaedah, mengambil kira jenis parameter.
getAnnotations() Mengembalikan anotasi untuk kaedah, termasuk anotasi induk.
getDeclaredAnnotations() Mengembalikan anotasi untuk kaedah tersebut, tidak termasuk anotasi induk.

Untuk mendapatkan tatasusunan kaedah kehendak kelas kami, kami boleh memanggil kaedah ini:


Method[] methods = Person.class.getDeclaredMethods();

Ia akan memberi kita semua kaedah dalam kelas kita.

kaedah getName() dan getModifiers().

Kita boleh menggunakan getName untuk mendapatkan nama setiap kaedah:


static List<String> getMethodsName(Method[] methods) {
    return Arrays.asList(methods)
            .stream()
            .map(Method::getName)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}

Sekarang untuk mendapatkan pengubah suai, mari tulis kaedah yang menggunakan getModifiers :


static List<String> getModifiers(Method[] methods) {
    return Arrays
            .stream(methods)
            .map(Method::getModifiers)
            .map(String::valueOf)
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}

Inilah kaedah utama kami :


public static void main(String[] args) {
    Method[] methods = Person.class.getDeclaredMethods();

    System.out.println(getMethodsName(methods));
    System.out.println(getModifiers(methods));
}

Keputusan kami:

[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Semua kaedah kami mempunyai pengubah suai awam , jadi kaedah terakhir mengembalikan pelbagai kaedah. Jika kami mengubah suai kod kami, kami akan melihat pengubah suai kami sendiri:


public static void main(String[] args) {
    Method[] methods = Person.class.getDeclaredMethods();

    System.out.println(getMethodsName(methods));
    System.out.println(modifyModifiers(getModifiers(methods)));
}
[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress] [public, public, public, public, public, public, public, public,
public, public, public]

getReturnedType()

Kaedah ini membolehkan kami mendapatkan jenis pemulangan kaedah:


static void getReturnedType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getReturnType)
            .forEach(System.out::println);
}
class java.lang.String
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void

getGenericReturnType()

Mari berikan kelas Person kami kaedah yang mengembalikan jenis yang dibalut dalam jenis parameter dan cuba dapatkan nilai pulangannya:


public List<String> someMethod() {
    // Very useful and important method
    return null;
}

Dan kami akan mengemas kini kaedah utama kami:


static void getGenericReturnType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getGenericReturnType)
            .forEach(System.out::println);
}

Hasil kaedah kami:

class java.lang.String
boolean
class java.lang.String int
void
class
java.lang.String
boolean
int
void
void java.util.List<java.lang.String>

kaedah getParameterTypes() dan getGenericParameterTypes().

Kami terus mengubah suai kelas Person kami , menambah dua lagi kaedah:


public List<String> someMethod(List<String> list, String s) {
    // Very useful and important method
    return null;
}

Yang pertama akan membolehkan kami mendapatkan parameter kaedah kami, dan yang kedua akan memberi kami jenis parameter juga.


static void getParameterTypes(Method[] methods) {
    Class<?>[] types = method.getParameterTypes();
        for (Class<?> type : types) {
            System.out.println(type);
        }
}

static void getGenericParameterTypes(Method[] methods) {
   Type[] types = method.getGenericParameterTypes();
        for (Type type : types) {
            System.out.println(type);
        }
}

Kami akan mengakses hanya satu daripada kaedah kami. Untuk mengakses kaedah dengan nama tertentu, kami memanggil getMethod dan masukkan nama dan parameter kaedah yang kami mahu:


public static void main(String[] args) throws NoSuchMethodException {
    Method currentMethod = Person.class.getMethod("someMethod", List.class, String.class);

    getParameterTypes(currentMethod);
    System.out.println();
    getGenericParameterTypes(currentMethod);
}

Menjalankan kod kami, kami akan melihat bagaimana kaedah berbeza dan apa yang mereka kembalikan:

antara muka java.util.List
class java.lang.String

java.util.List<java.lang.String>
class java.lang.String

kaedah getExceptionTypes() dan getGenericExceptionTypes().

Kami boleh menggunakan kaedah ini untuk mendapatkan pelbagai pengecualian yang kaedah kami boleh lemparkan, serta pengecualian dengan jenis parameter (jika ada). Mari kita gunakan contoh baharu yang mempunyai kelas statik tersembunyi:


private static class Processor {
    private void init() {}

    private void process() throws IOException {}
}

Dan kami akan memanggil kaedah pada kelas Pemproses kami:


public static void main(String... args) throws NoSuchMethodException {
    Method method = Processor.class.getDeclaredMethod("process");
    Type[] type = method.getExceptionTypes();
    System.out.println(Arrays.toString(type));
}

Sekarang kita dapat melihat pengecualian kita:

[class java.io.IOException]

Sekarang mari kita parameterkan jenis. Kami akan mengubah suai kelas utama kami:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Dan kod kaedah utama :


public static void main(String... args) throws NoSuchMethodException {
    Method m = Processor.class.getDeclaredMethod("process");
    Type[] t = m.getGenericExceptionTypes();
    System.out.println(Arrays.toString(t));

    for (Type type : t) {
        if (type instanceof TypeVariable) {
            for (Type type1 : ((TypeVariable) type).getBounds()) {
                System.out.println(type1);
            }
        }
    }
}

Di dalam kaedah ini, kami mendapat objek TypeVariables , yang merupakan antara muka induk generik untuk pembolehubah jenis. Dan di dalamnya, kami kini boleh mendapatkan parameter dalaman, iaitu pengecualian bersarang kami:

[E]
kelas java.io.IOException

kaedah getAnnotations() dan getDeclaredAnnotations().

Mari teruskan menggunakan kelas baharu ini dan tambahkan beberapa anotasi padanya. Kami akan membuat anotasi Anotasi kami sendiri :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

    public String key();
    public String value();
}

Dan gunakannya pada kaedah kami:


@Annotation(key = "key", value = "value")
private void process() throws E{

}

Dan sudah tentu kami akan menambah kaedah untuk memaparkan semua anotasi kami:


static void getMethodAnnotations(Class<?> clazz) {
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println(method.getName());
        System.out.println(Arrays.toString(method.getAnnotations()));
        System.out.println();
    }
}

Pelaksanaan kaedah utama kami :


public static void main(String... args) {
    Class clazz = Processor.class;
    getMethodAnnotations(clazz);
}

Output skrin yang terhasil ialah:

proses
[@com.company.Main&Annotation(key="key", value="value")]

Beginilah cara kami boleh mendapatkan anotasi yang telah digunakan pada kaedah kami, dan kaedah getAnnotations membolehkan kami mengakses anotasi induk kelas itu juga.

Hari ini kita berkenalan dengan cara refleksi boleh berfungsi dengan kaedah dan medan, dan data apa yang boleh kita peroleh dengannya!