CodeGym /หลักสูตรจาวา /โมดูล 2: Java Core /การรับข้อมูลโดยใช้การสะท้อนกลับ

การรับข้อมูลโดยใช้การสะท้อนกลับ

โมดูล 2: Java Core
ระดับ , บทเรียน
มีอยู่

คลาส java.lang.reflect.Field

คลาสฟิลด์ให้ข้อมูลเกี่ยวกับและการเข้าถึงแบบไดนามิกไปยังฟิลด์เดียวของคลาสหรืออินเทอร์เฟซ ฟิลด์ยังอนุญาตให้มีการแปลงประเภทที่กว้างขึ้นระหว่างการดำเนินการรับหรือตั้งค่าการเข้าถึง แต่จะโยนIllegalArgumentExceptionหากการจำกัดเกิดขึ้น

ในการรับ วัตถุ ฟิลด์ก่อนอื่นเราจะเขียนคลาส:


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

นี่คือวิธีที่เราได้รับรายชื่อฟิลด์ของชั้นเรียน ซึ่งเราจะดำเนินการในภายหลัง นี่คือผลลัพธ์:

[ส่วนตัว java.lang.String com.company.Person.name, ส่วนตัว int com.company.Person.age, บูลีนสาธารณะ com.company.Person.isMale, ป้องกัน java.lang.String com.company.Person.address, สาธารณะ int สุดท้ายคงที่ com.company.Person.MAX_AGE, int สุดท้ายคงที่สาธารณะ com.company.Person.MIN_AGE]

ทีนี้ มาดูกันว่าเราทำอะไรกับข้อมูลนี้ได้บ้าง พูดคุยเกี่ยวกับวิธีการของ คลาส Field :

วิธี คำอธิบาย
รับประเภท () ส่งคืนวัตถุคลาสที่ระบุประเภทการประกาศของฟิลด์ที่แสดงโดยสิ่งนี้สนามวัตถุ.
getAnnotatedType() ส่งคืนคำอธิบายประกอบประเภทวัตถุที่แสดงถึงการใช้ประเภทเพื่อระบุประเภทการประกาศของฟิลด์ที่แสดงโดยฟิลด์นี้
getGenericType() คืนพิมพ์วัตถุที่แสดงถึงประเภทของฟิลด์ที่ประกาศซึ่งแสดงโดยสิ่งนี้สนามวัตถุ.
รับชื่อ () ส่งกลับชื่อของฟิลด์ที่แสดงโดยสิ่งนี้สนามวัตถุ.
getModifiers() ส่งกลับ int เข้ารหัสตัวดัดแปลงภาษา Java สำหรับฟิลด์ที่แสดงโดยสิ่งนี้สนามวัตถุ.
รับคำอธิบายประกอบ () ส่งกลับคำอธิบายประกอบสำหรับฟิลด์นี้ หากไม่มีคำอธิบายประกอบ จะส่งคืนอาร์เรย์ว่าง

เมธอด getType(), getName() และ getModifiers()

เราสามารถใช้ เมธอด getType()เพื่อรับประเภทของฟิลด์ของเรา ลองเขียนวิธีการ:


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

และเราได้ผลลัพธ์นี้:

คลาส java.lang.String
int
บูลีน
คลาส java.lang.String
int
int

ตอนนี้ให้เพิ่มวิธีการในชั้นเรียนของเราเพื่อรับชื่อฟิลด์ สิ่งนี้จะทำให้การนำทางในฟิลด์ของชั้นเรียนของเราง่ายขึ้น


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

ตอนนี้ผลลัพธ์เป็นที่เข้าใจมากขึ้น:

ประเภทฟิลด์ - คลาส java.lang.String
ชื่อฟิลด์ -

ชื่อ ประเภทฟิลด์ - int
ชื่อฟิลด์ - อายุ

ประเภท
ฟิลด์ - บูลีน ชื่อฟิลด์ - isMale

ประเภทฟิลด์ - คลาส java.lang.String
ชื่อฟิลด์ - ที่อยู่

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MAX_AGE

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MIN_AGE

ยอดเยี่ยม! มาปรับเปลี่ยนวิธีการของเรากันดีกว่า! เราจะเพิ่มตัวแก้ไขการเข้าถึงที่นี่


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

และมาดูกันว่าe.getModifiers()ส่งคืน อะไร เมธอดนี้ส่งคืนintที่ช่วยให้เรากำหนดสถานะของตัวแก้ไขการเข้าถึงฟิลด์ของเรา คลาส Modifier ประกอบด้วยตัวแปรสแตติกที่รับผิดชอบสำหรับตัวแก้ไขเฉพาะแต่ละ ตัวของฟิลด์

มารวมค่าส่งคืนของเราในModifier.toString()และรับค่าเป็นข้อความทันที:

ประเภทฟิลด์ - คลาส java.lang.String
ชื่อฟิลด์ - ชื่อ โม
ดิฟายเออร์ - ส่วนตัว

ประเภทฟิลด์ - int
ชื่อฟิลด์ - อายุ โม
ดิฟายเออร์ - ส่วนตัว

ประเภทฟิลด์ - ชื่อฟิลด์บูลีน
- isMale
โมดิฟายเออร์ - สาธารณะ

ประเภทฟิลด์ - คลาส java.lang.String
ชื่อฟิลด์ - ตัวแก้ไข ที่อยู่
- ป้องกัน

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MAX_AGE
ตัวแก้ไข - สาธารณะแบบคงที่สุดท้าย

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MIN_AGE
ตัวแก้ไข - สาธารณะแบบคงที่สุดท้าย

นี่คือลักษณะที่ไม่มีModifier.toString() :

ประเภทฟิลด์ - คลาส java.lang.String
ชื่อฟิลด์ - ชื่อ
ตัวดัดแปลง - 2

ประเภทฟิลด์ - int
ชื่อฟิลด์ - อายุ โม ดิฟาย
เออร์ - 2

ประเภทฟิลด์ - บูลีน
ชื่อฟิลด์ - isMale
ตัวดัดแปลง - 1

ประเภทฟิลด์ - class java.lang.String
ชื่อฟิลด์ - ตัวแก้ไข ที่อยู่
- 4

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MAX_AGE
ตัวแก้ไข - 25

ประเภทฟิลด์ - int
ชื่อฟิลด์ - MIN_AGE
ตัวแก้ไข - 25

เมธอด getAnnotations(), getAnnotatedType() และ getGenericType()

มาแก้ไข คลาส Personเพื่อทำงานกับเมธอดเหล่านี้ เราจะเขียนคำอธิบายประกอบของเราเองและนำไปใช้กับฟิลด์ของเรา และเราจะเพิ่มฟิลด์อีกสองสามฟิลด์

มาสร้างคำอธิบายประกอบสองรายการ เราจะส่งชื่อตัวแปรในภาษา Pig Latin ไปที่หนึ่ง และเราจะใช้ชื่อที่สองสำหรับองค์ประกอบ:


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

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

และเราจะเปลี่ยนคลาสหลักและ คลาส บุคคล :


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

ถึงเวลาดูผลลัพธ์ของวิธีการของเราและค้นหาว่าใช้ไปเพื่ออะไร:

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 > คลาส
java.lang.String \u006b\u006e\u0061\u006d\u0065\u0073")] [@Name(name="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [ ]






  • getAnnotatedTypeส่งคืนคำอธิบายประกอบสำหรับฟิลด์ที่กำหนด ถ้ามี เราได้รับคำอธิบายประกอบสำหรับฟิลด์และเราสามารถมองเห็นได้อย่างสมบูรณ์

  • getGenericTypeช่วยให้คุณแสดงประเภทพารามิเตอร์ได้อย่างถูกต้อง

  • getAnnotationsส่งคืนคำอธิบายประกอบที่ใช้กับวัตถุของเรา

นี่คือวิธีที่เราสามารถรับข้อมูลทั้งหมดเกี่ยวกับแต่ละฟิลด์ในชั้นเรียนของเรา ตลอดจนตัวแก้ไขการเข้าถึง คำอธิบายประกอบ และประเภทข้อมูลได้อย่างง่ายดาย

java.lang.reflect.Method คลาส

สุดยอด! เราได้พูดคุยเกี่ยวกับสาขาของชั้นเรียนของเรา ตอนนี้ถึงเวลาที่จะพูดถึงวิธีการ

ในการรับ วัตถุ เมธอดเราเรียก เมธอด getMethodโดยส่งเป็นชื่อเมธอดของเรา นี่เป็นวิธีพื้นฐานในการรับ วัตถุ เมธอด :


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

เราจะทำงานกับชั้นเรียนของเราต่อไป มาเพิ่ม getters และ setters และ hashCode วิธีเท่ากับและ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);
    }
}

ตอนนี้เรามาเตรียมชุดของวิธีการที่เราจะตรวจสอบโดยใช้คลาสเมธอด นี่คือรายการของวิธีการที่สำคัญที่สุด:

วิธี คำอธิบาย
รับชื่อ () ส่งกลับชื่อของวิธีการ
getModifiers() ส่งคืนตัวแก้ไขการเข้าถึงของเมธอดนี้
getReturnType() ส่งคืนประเภทการส่งคืนของเมธอด
getGenericReturnType() ส่งคืนประเภทการส่งคืนของเมธอด บัญชีสำหรับเมธอดทั่วไป
getParameterTypes() ส่งคืนอาร์เรย์ของพารามิเตอร์เมธอด
getGenericParameterTypes() ส่งคืนอาร์เรย์ของพารามิเตอร์เมธอด บัญชีสำหรับเมธอดทั่วไป
getExceptionTypes() ส่งกลับข้อยกเว้นที่เมธอดสามารถส่งกลับได้
getGenericExceptionTypes() ส่งกลับข้อยกเว้นที่เมธอดโยนได้ บัญชีสำหรับประเภทพารามิเตอร์
รับคำอธิบายประกอบ () ส่งกลับคำอธิบายประกอบสำหรับวิธีการ รวมถึงคำอธิบายประกอบพาเรนต์
getDeclaredAnnotations() ส่งกลับคำอธิบายประกอบสำหรับวิธีการ ไม่รวมคำอธิบายประกอบพาเรนต์

ในการรับอาร์เรย์ของเมธอดของคลาสของเรา เราสามารถเรียกเมธอดนี้ว่า:


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

มันจะให้วิธีการทั้งหมดในชั้นเรียนของเรา

เมธอด getName() และ getModifiers()

เราสามารถใช้getNameเพื่อรับชื่อของแต่ละวิธี:


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

ตอนนี้เพื่อรับตัวดัดแปลงมาเขียนเมธอดที่ใช้getModifiers :


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

นี่คือ วิธีการ หลัก ของเรา :


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

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

ผลลัพธ์ของเรา:

[getName, เท่ากับ, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

ทุกเมธอดของเรามี ตัวดัดแปลง สาธารณะดังนั้นเมธอดสุดท้ายจึงคืนอาร์เรย์ของเมธอด หากเราแก้ไขรหัสของเรา เราจะเห็นตัวแก้ไขของเราเอง:


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

    System.out.println(getMethodsName(methods));
    System.out.println(modifyModifiers(getModifiers(methods)));
}
[getName, เท่ากับ, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress] [สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ สาธารณะ
สาธารณะ]

getReturnedType()

วิธีนี้ช่วยให้เราได้รับประเภทผลตอบแทนของวิธีการ:


static void getReturnedType(Method[] methods) {
    Arrays.stream(methods)
            .map(Method::getReturnType)
            .forEach(System.out::println);
}
คลาส java.lang.String
บูลีน
คลาส java.lang.String
int
เป็นโมฆะ
คลาส java.lang.String
บูลีน
int
เป็น
โมฆะ
เป็นโมฆะ

getGenericReturnType()

ให้ เมธอดคลาส Person ของเรา คืนค่าประเภทที่ห่อด้วยพารามิเตอร์ที่กำหนด และลองรับค่าส่งคืน:


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

และเราจะอัปเดตวิธีการหลักของเรา:


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

ผลลัพธ์ของวิธีการของเรา:

คลาส java.lang.String
บูลีน
คลาส java.lang.String
int
เป็นโมฆะ
คลาส java.lang.String
บูลี
น int
เป็น
โมฆะ
เป็นโมฆะ
java.util.List<java.lang.String>

เมธอด getParameterTypes() และ getGenericParameterTypes()

เรายังคงแก้ไข คลาส บุคคล ของเรา โดยเพิ่มเมธอดอีกสองเมธอด:


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

อันแรกจะให้เราได้รับพารามิเตอร์ของวิธีการของเรา และอันที่สองจะให้ประเภทพารามิเตอร์เช่นกัน


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

เราจะเข้าถึงวิธีใดวิธีหนึ่งของเราเท่านั้น ในการเข้าถึงเมธอดด้วยชื่อเฉพาะ เราเรียกgetMethodและส่งผ่านชื่อและพารามิเตอร์ของเมธอดที่เราต้องการ:


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

รันโค้ดของเรา เราจะดูว่าเมธอดต่างกันอย่างไรและได้อะไรกลับมา:

อินเตอร์เฟส java.util.List
คลาส java.lang.String

java.util.List<java.lang.String>
คลาส java.lang.String

เมธอด getExceptionTypes() และ getGenericExceptionTypes()

เราสามารถใช้เมธอดเหล่านี้เพื่อรับอาร์เรย์ของข้อยกเว้นที่เมธอดของเราสามารถส่งได้ เช่นเดียวกับข้อยกเว้นที่มีประเภทพารามิเตอร์ (ถ้ามี) ลองใช้ตัวอย่างใหม่ที่มีคลาสสแตติกซ่อนอยู่:


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

    private void process() throws IOException {}
}

และเราจะเรียกเมธอดใน คลาส ตัวประมวลผล ของเรา :


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

ตอนนี้เราสามารถเห็นข้อยกเว้นของเรา:

[คลาส java.io.IOException]

ทีนี้มากำหนดพารามิเตอร์ของประเภทกัน เราจะแก้ไขคลาสหลักของเรา:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

และรหัสของ วิธีการ หลัก :


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

ภายในวิธีนี้ เราได้ วัตถุ TypeVariablesซึ่งเป็นอินเทอร์เฟซหลักทั่วไปสำหรับตัวแปรประเภท และภายในนั้น ตอนนี้เราสามารถรับพารามิเตอร์ภายในได้ ซึ่งก็คือข้อยกเว้นแบบซ้อนของเรา:

[E]
คลาส java.io.IOException

เมธอด getAnnotations() และ getDeclaredAnnotations()

ลองใช้คลาสใหม่นี้ต่อไปและเพิ่มคำอธิบายประกอบสองสามข้อ เราจะสร้าง คำอธิบาย ประกอบคำอธิบายประกอบ ของเราเอง :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

และนำไปใช้กับวิธีการของเรา:


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

}

และแน่นอน เราจะเพิ่มวิธีการแสดงคำอธิบายประกอบทั้งหมดของเรา:


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

การดำเนินการตาม วิธีการ หลัก ของเรา :


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

เอาต์พุตหน้าจอที่ได้คือ:

ประมวลผล
[@com.company.Main&Annotation(key="key", value="value")]

นี่คือวิธีที่เราจะได้รับคำอธิบายประกอบที่ใช้กับเมธอดของเรา และ เมธอด getAnnotationsช่วยให้เราเข้าถึงคำอธิบายประกอบหลักของคลาสได้เช่นกัน

วันนี้เราได้ทำความคุ้นเคยกับวิธีการสะท้อนกลับที่ทำงานร่วมกับเมธอดและฟิลด์ต่างๆ และข้อมูลใดบ้างที่เราจะได้รับจากมัน!

ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION