lớp java.lang.reflect.Field

Lớp Trường cung cấp thông tin và quyền truy cập động vào một trường duy nhất của một lớp hoặc giao diện. Trường cũng cho phép chuyển đổi loại mở rộng trong thao tác truy cập get hoặc set, nhưng sẽ ném IllegalArgumentException nếu việc thu hẹp xảy ra.

Để có được một đối tượng Trường , trước tiên chúng ta sẽ viết một lớp:


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;
}

Và đây là mã của chúng tôi để làm việc với lớp đó:


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);
    }
}

Đây là cách chúng ta lấy danh sách các trường của lớp mà chúng ta sẽ làm việc sau này. Đây là kết quả:

[riêng java.lang.String com.company.Person.name, private int com.company.Person.age, boolean công khai com.company.Person.isMale, java.lang.String được bảo vệ com.company.Person.address, công khai int cuối cùng tĩnh com.company.Person.MAX_AGE, cuối cùng tĩnh công khai int com.company.Person.MIN_AGE]

Bây giờ hãy xem chúng ta có thể làm gì với dữ liệu này. Hãy nói về các phương thức của lớp Field :

Phương pháp Sự miêu tả
getType() Trả về một đối tượng Lớp xác định loại trường đã khai báo được đại diện bởi đối tượng nàyCánh đồngsự vật.
getAnnotatedType() Trả về mộtLoại chú thíchđối tượng đại diện cho việc sử dụng một loại để chỉ định loại khai báo của trường được đại diện bởi Trường này.
getGenericType() Trả về mộtKiểuđối tượng đại diện cho loại khai báo của trường được đại diện bởi điều nàyCánh đồngsự vật.
getName() Trả về tên của trường được đại diện bởi cái nàyCánh đồngsự vật.
getModifier() Trả về một int mã hóa các công cụ sửa đổi ngôn ngữ Java cho trường được đại diện bởi điều nàyCánh đồngsự vật.
getAnnotations() Trả về chú thích cho trường này. Nếu không có chú thích, nó sẽ trả về một mảng trống.

các phương thức getType(), getName() và getModifiers()

Chúng ta có thể sử dụng phương thức getType() để lấy kiểu của trường. Hãy viết một phương thức:


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

Và chúng tôi nhận được kết quả này:

lớp java.lang.String
int
boolean
lớp java.lang.String
int
int

Bây giờ, hãy thêm một phương thức vào lớp của chúng ta để lấy tên của một trường. Điều này sẽ giúp điều hướng các trường của lớp chúng ta dễ dàng hơn.


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

Bây giờ kết quả là dễ hiểu hơn:

Loại trường - lớp java.lang.String
Tên trường - tên

Loại trường - int
Tên trường - tuổi

Loại trường - boolean
Tên trường - isMale

Loại trường - lớp java.lang.String
Tên trường - địa chỉ

Loại trường - int
Tên trường - MAX_AGE

Loại trường - int
Tên trường - MIN_AGE

Tuyệt vời! Hãy sửa đổi phương pháp của chúng tôi một số chi tiết! Chúng tôi sẽ thêm công cụ sửa đổi quyền truy cập tại đây


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())));
}

Và hãy xem những gì e.getModifiers() trả về. Phương thức này trả về một int cho phép chúng tôi xác định trạng thái của công cụ sửa đổi truy cập trường của chúng tôi. Lớp Công cụ sửa đổi chứa các biến tĩnh chịu trách nhiệm cho từng công cụ sửa đổi cụ thể của trường.

Hãy bọc giá trị trả về của chúng ta trong Modifier.toString() và ngay lập tức nhận giá trị của nó dưới dạng văn bản:

Loại trường - lớp java.lang.String
Tên trường - tên Công cụ
sửa đổi - riêng tư

Loại trường - int
Tên trường - tuổi
Công cụ sửa đổi - riêng tư

Loại trường - boolean
Tên trường - isMale
Công cụ sửa đổi - công khai

Loại trường - lớp java.lang.String
Tên trường - address
Công cụ sửa đổi - protected

Loại trường - int
Tên trường - MAX_AGE Công cụ
sửa đổi - public static final

Loại trường - int
Tên trường - MIN_AGE
Công cụ sửa đổi - public static final

Đây là giao diện của nó khi không có Modifier.toString() :

Loại trường - lớp java.lang.String
Tên trường - tên Công cụ
sửa đổi - 2

Loại trường - int
Tên trường - tuổi Công cụ
sửa đổi - 2

Loại trường - boolean
Tên trường - isMale Công
cụ sửa đổi - 1

Loại trường - lớp java.lang.String
Tên trường - address
Công cụ sửa đổi - 4

Loại trường - int
Tên trường - MAX_AGE
Công cụ sửa đổi - 25

Loại trường - int
Tên trường - MIN_AGE
Công cụ sửa đổi - 25

các phương thức getAnnotations(), getAnnotatedType() và getGenericType()

Hãy sửa đổi lớp Person để làm việc với các phương thức này. Chúng tôi sẽ viết chú thích của riêng mình và áp dụng nó vào các trường của chúng tôi. Và chúng tôi sẽ thêm một vài trường nữa.

Hãy tạo hai chú thích. Chúng tôi sẽ chuyển tên biến trong Pig Latin thành một và chúng tôi sẽ sử dụng tên thứ hai cho các phần tử:


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

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

Và chúng ta sẽ thay đổi lớp chính và lớp Người :


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())));
}

Đã đến lúc xem xét kết quả của các phương pháp của chúng tôi và tìm hiểu xem chúng dùng để làm gì:

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

getGenericType:
java.lang.Class<java.lang. Object>
java.util.List<java.lang.String>
class java.lang.String
class [I

getAnnotations:
[]
[@Name(name="\u0055\u0073\u0065\u0072\u0020\u006e\u0069\u0063 \u006b\u006e\u0061\u006d\u0065\u0073")]
[@Name(name="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [
]
  • getAnnotatedType trả về chú thích cho trường đã cho, nếu có. Chúng tôi nhận được chú thích cho trường và chúng tôi có thể nhìn thấy nó một cách hoàn hảo.

  • getGenericType cho phép bạn hiển thị chính xác loại được tham số hóa.

  • getAnnotations trả về các chú thích được áp dụng cho đối tượng của chúng ta.

Đây là cách chúng ta có thể dễ dàng lấy tất cả dữ liệu về từng trường trong lớp của mình, cũng như các công cụ sửa đổi truy cập, chú thích và kiểu dữ liệu của nó.

lớp java.lang.reflect.Method

Siêu! Chúng tôi đã nói về các lĩnh vực của lớp học của chúng tôi. Bây giờ là lúc để nói về các phương pháp.

Để lấy một đối tượng Phương thức , chúng ta gọi phương thức getMethod , truyền cho nó tên của phương thức của chúng ta. Đây là cách cơ bản để lấy một đối tượng Method :


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

Chúng tôi sẽ tiếp tục làm việc với lớp học của chúng tôi. Hãy thêm các phương thức getters và setters, hashCode, equalstoString :


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);
    }
}

Bây giờ, hãy chuẩn bị một tập hợp các phương thức mà chúng ta sẽ kiểm tra bằng cách sử dụng lớp Phương thức . Dưới đây là danh sách các phương pháp quan trọng nhất:

Phương pháp Sự miêu tả
getName() Trả về tên của phương thức.
getModifier() Trả về công cụ sửa đổi truy cập của phương pháp này.
getReturnType() Trả về kiểu trả về của phương thức.
getGenericReturnType() Trả về kiểu trả về của phương thức, chiếm các phương thức chung.
getParameterTypes() Trả về một mảng các tham số của phương thức.
getGenericParameterTypes() Trả về một mảng các tham số của phương thức, chiếm các phương thức chung.
getExceptionTypes() Trả về các ngoại lệ mà phương thức có thể ném ra.
getGenericExceptionTypes() Trả về các ngoại lệ mà phương thức có thể đưa ra, chiếm các kiểu được tham số hóa.
getAnnotations() Trả về các chú thích cho phương thức, bao gồm cả các chú thích cha.
getDeclaredAnnotations() Trả về chú thích cho phương thức, không bao gồm chú thích gốc.

Để có được một mảng các phương thức của lớp chúng ta, chúng ta có thể gọi phương thức này:


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

Nó sẽ cung cấp cho chúng ta tất cả các phương thức trong lớp của chúng ta.

phương thức getName() và getModifiers()

Chúng ta có thể sử dụng getName để lấy tên của từng phương thức:


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

Bây giờ để có được các công cụ sửa đổi, hãy viết một phương thức sử dụng getModifiers :


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

Đây là phương pháp chính của chúng tôi :


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

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

Kết quả của chúng tôi:

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

Tất cả các phương thức của chúng tôi đều có công cụ sửa đổi công khai , vì vậy phương thức cuối cùng trả về một mảng các phương thức. Nếu chúng tôi sửa đổi mã của mình, chúng tôi sẽ thấy chính các công cụ sửa đổi của chúng tôi:


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] [công khai, công khai, công khai, công khai, công khai, công khai, công khai, công khai, công khai, công khai, công
khai]

getReturnedType()

Phương thức này cho phép chúng ta lấy kiểu trả về của phương thức:


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

getGenericReturnType()

Hãy cung cấp cho lớp Person của chúng ta một phương thức trả về kiểu được bao bọc trong kiểu được tham số hóa và thử lấy giá trị trả về của nó:


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

Và chúng tôi sẽ cập nhật phương pháp chính của chúng tôi:


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

Kết quả của phương pháp của chúng tôi:

lớp java.lang.String boolean
lớp
java.lang.String
int
void
lớp java.lang.String
boolean
int
void
void java.util.List<java.lang.String>

phương thức getParameterTypes() và getGenericParameterTypes()

Chúng tôi tiếp tục sửa đổi lớp Person của mình , thêm hai phương thức nữa:


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

Cái đầu tiên sẽ cho phép chúng ta lấy các tham số của các phương thức của chúng ta và cái thứ hai cũng sẽ cung cấp cho chúng ta các loại được tham số hóa.


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);
        }
}

Chúng tôi sẽ chỉ truy cập một trong các phương pháp của chúng tôi. Để truy cập một phương thức bằng một tên cụ thể, chúng tôi gọi getMethod và chuyển vào tên và tham số của phương thức mà chúng tôi muốn:


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);
}

Chạy mã của chúng ta, chúng ta sẽ thấy các phương thức khác nhau như thế nào và chúng trả về cái gì:

giao diện java.util.List
lớp java.lang.String

java.util.List<java.lang.String>
lớp java.lang.String

các phương thức getExceptionTypes() và getGenericExceptionTypes()

Chúng ta có thể sử dụng các phương thức này để lấy một loạt các ngoại lệ mà phương thức của chúng ta có thể đưa ra, cũng như các ngoại lệ với các kiểu được tham số hóa (nếu có). Hãy sử dụng một ví dụ mới có một lớp tĩnh ẩn:


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

    private void process() throws IOException {}
}

Và chúng tôi sẽ gọi các phương thức trên lớp Bộ xử lý của chúng tôi:


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

Bây giờ chúng ta có thể thấy ngoại lệ của mình:

[lớp java.io.IOException]

Bây giờ hãy tham số hóa loại. Chúng tôi sẽ sửa đổi lớp chính của chúng tôi:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Và mã của phương thức chính :


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);
            }
        }
    }
}

Bên trong phương thức này, chúng ta có một đối tượng TypeVariables , là một giao diện gốc chung cho các biến kiểu. Và bên trong đó, bây giờ chúng ta có thể lấy tham số bên trong, cụ thể là ngoại lệ lồng nhau của chúng ta:

[E]
lớp java.io.IOException

phương thức getAnnotations() và getDeclaredAnnotations()

Hãy tiếp tục sử dụng lớp mới này và thêm một vài chú thích cho nó. Chúng ta sẽ tạo chú thích Annotation của riêng mình :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Và áp dụng nó cho phương pháp của chúng tôi:


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

}

Và tất nhiên, chúng tôi sẽ thêm một phương thức để hiển thị tất cả các chú thích của chúng tôi:


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();
    }
}

Thực hiện phương pháp chính của chúng tôi :


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

Đầu ra màn hình kết quả là:

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

Đây là cách chúng ta có thể nhận được các chú thích đã được áp dụng cho các phương thức của mình và phương thức getAnnotations cũng cho phép chúng ta truy cập các chú thích gốc của lớp.

Hôm nay chúng ta đã làm quen với cách phản chiếu có thể hoạt động với các phương thức và trường cũng như dữ liệu nào chúng ta có thể nhận được với nó!