JavaRush /Курсы /All lectures for AM purposes /Տվյալների ստացում ռեֆլեքսիայի միջոցով

Տվյալների ստացում ռեֆլեքսիայի միջոցով

All lectures for AM purposes
1 уровень , 82 лекция
Открыта

Կլասը java.lang.reflect.Field

Կլասը Field տրամադրում է տեղեկատվություն և դինամիկ մուտք դեպի դասի կամ ինտերֆեյսի մեկ դաշտ: Field նույնպես թույլ է տալիս ընդլայնում փոխակերպումների ժամանակ get կամ set մուտքի աշխատանքում, բայց տեղադրում է IllegalArgumentException բացառություն, եթե տեղի է ունենում նեղացում փոխակերպման ժամանակ:

Որպեսզի ստանաք Filed կլասը, մենք կգրենք կլասը, որի հետ կաշխատենք, ինչպես նաև դրա համար ցուցիչ:


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

Այսպիսով մենք ստանում ենք մեր դասի դաշտերի ցանկը, որոնց հետ կշարունակենք աշխատել: Արդյունքը տեսք կունենա այսպես:

[private java.lang.String 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]

Հիմա եկեք տեսնենք, թե ինչ կարող ենք անել այս տվյալների շրջանակի հետ: Խոսենք Field կլասի մեթոդների մասին:

Մեթոդ Նկարագրություն
getType() Վերադարձնում է դասի օբյեկտ, որը որոշում է հայտարարված դաշտի տեսակն այս Field օբյեկտում:
getAnnotatedType() Վերադարձնում է AnnotatedType օբյեկտ, որը ներկայացնում է դաշտի հայտարարված տիպը այս դաշտում:
getGenericType() Վերադարձնում է Type օբյեկտ, որը ներկայացնում է դաշտի հայտարարված տիպը այս Field օբյեկտում:
getName() Վերադարձնում է դաշտի անունը, որը ներկայացված է այս Field օբյեկտում:
getModifiers() Վերադարձնում է Java լեզվի մոդիֆիկատորները դաշտի համար, որը ներկայացված է այս Field օբյեկտում, որպես ամբողջական թիվ:
getAnnotations() Վերադարձնում է այս դաշտի անոտացիաները: Եթե անոտացիաներ չկան — դատարկ զանգված:

Մեթոդներ getType(), getName(), getModifiers()

Օգտագործելով getType() մեթոդը, մենք կարող ենք ստանալ մեր դաշտի տեսակն: Եկեք գրենք մեթոդ:


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

Եվ կստանանք այսպիսի արդյունք:

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

Եկեք անմիջապես մեր դասում ավելացնենք մեթոդ, որը կստացնի դաշտի անունը: Այսպես ավելի հեշտ կլինի կողմնորոշվել մեր դասի դաշտերի միջոցով:


static void printTypesAndNames(List<Field> fields){
   fields.forEach(e -> System.out.printf("Դաշտի տեսակ - %s\nԴաշտի անուն - %s\n\n", e.getType(), e.getName()));
}

Արդյունքը կդառնա ավելի հասկանալի օգտագործողի համար:

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - name

Դաշտի տեսակ - int
Դաշտի անուն - age

Դաշտի տեսակ - boolean
Դաշտի անուն - isMale

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - address

Դաշտի տեսակ - int
Դաշտի անուն - MAX_AGE

Դաշտի տեսակ - int
Դաշտի անուն - MIN_AGE

Հիանալի, եկեք նաև մեր մեթոդը ավելի մոդիֆիկացնենք: Ավելացնենք այստեղ մուտքի մոդիֆիկատորները:


static void printFieldInfo(List<Field> fields){
   fields.forEach(e -> System.out.printf("Դաշտի տեսակ - %s\nԴաշտի անուն - %s\nՄոդիֆիկատորներ - %s\n\n", e.getType(), e.getName(), Modifier.toString(e.getModifiers())));
}

Եվ եկեք քննարկենք, թե ինչ է վերադարձնում e.getModifiers(): Այս մեթոդը վերադարձնում է int թիվ, որի մեջ կարող ենք որոշել դաշտի մուտքի մոդիֆիկատորը: Modifier կլասում կան ստատիկ փոփոխականներ, որոնք պատասխանատու են կոնկրետ մոդիֆիկատորի համար:

Մեր վերադարձվելիք արժեքը կտեղավորենք Мodifier.toString() — և անմիջապես կստանանք արժեքը տեքստային տեսքով:

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - name
Մոդիֆիկատորներ - private

Դաշտի տեսակ - int
Դաշտի անուն - age
Մոդիֆիկատորներ - private

Դաշտի տեսակ - boolean
Դաշտի անուն - isMale
Մոդիֆիկատորներ - public

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - address
Մոդիֆիկատորներ - protected

Դաշտի տեսակ - int
Դաշտի անուն - MAX_AGE
Մոդիֆիկատորներ - public static final

Դաշտի տեսակ - int
Դաշտի անուն - MIN_AGE
Մոդիֆիկատորներ - public static final

Եվ այսպես է դա երևում առանց Мodifier.toString():

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - name
Մոդիֆիկատորներ - 2

Դաշտի տեսակ - int
Դաշտի անուն - age
Մոդիֆիկատորներ - 2

Դաշտի տեսակ - boolean
Դաշտի անուն - isMale
Մոդիֆիկատորներ - 1

Դաշտի տեսակ - class java.lang.String
Դաշտի անուն - address
Մոդիֆիկատորներ - 4

Դաշտի տեսակ - int
Դաշտի անուն - MAX_AGE
Մոդիֆիկատորներ - 25

Դաշտի տեսակ - int
Դաշտի անուն - MIN_AGE
Մոդիֆիկատորներ - 25

Մեթոդներ getAnnotations(), getAnnotatedType(), getGenericType()

Եկեք մոդիֆիկացնենք Person կլասը, որպեսզի աշխատենք ընթացիկ մեթոդների հետ: Մենք կգրենք մեր սեփական անոտացիան, որը կավելացնենք մեր դաշտերին, ինչպես նաև կավելացնենք մի քանի դաշտ:

Ստեղծենք երկու անոտացիաներ: Մեկին կփոխանցենք փոփոխականի անունը ռուսերենով, իսկ երկրորդը կօգտագործենք տարրերի համար:


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

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

Եվ փոփոխենք մեր հիմնական կլասը և Person: կլասը:


public class Person {
    @Name(name = "Имя")
    private String name;

    @Name(name = "Никнэймы пользователя")
    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>
class java.lang.String
class [I

getAnnotations:
[]
[@Name(name="\u041d\u0438\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f")]
[@Name(name="\u0418\u043c\u044f")]
[]
  • getAnnotatedType վերադարձնում է դաշտի անոտացիան, եթե կա: Մենք ունենք դաշտի անոտացիա և մենք դա լավ տեսնում ենք:

  • getGenericType թույլ է տալիս ճիշտ ցույց տալ գեներացված պարամետրերը.

  • getAnnotations վերադարձնում է անոտացիաները, որոնք դրված են մեր օբյեկտի վրա:

Այսպիսով մենք հեշտությամբ կարող ենք ստանալ բոլոր տվյալները մեր դասի յուրաքանչյուր դաշտի մասին, նրա մուտքի մոդիֆիկատորների մասին, անոտացիաների և տվյալների տեսակների մասին:

Կլասը java.lang.reflect.Method

Սուպեր, մենք խոսեցինք մեր դասի դաշտերի մասին, հիմա ժամանակն է խոսել մեթոդների մասին:

Որպեսզի ստանաք Method կլասի օբյեկտ, մենք կանչում ենք getMethod և դրան փոխանցում մեր մեթոդի անունը: Սա հիմնարար մեթոդն է Method կլասի ստացման համար:


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

Մենք կշարունակենք աշխատել մեր դասի հետ: Ավելացնենք անդամներից և set-երները, hashCode-երը, equals և 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);
    }
}

Եվ եկեք պատրաստենք մեթոդների հավաքածուն, որոնք մենք կքննարկենք Method կլասի մեջ: Ահա հիմնական մեթոդների ցուցակը:

Մեթոդ Նկարագրություն
getName() Վերադարձնում է մեթոդի անունը:
getModifiers() Վերադարձնում է մեթոդի մուտքի մոդիֆիկատորը:
getReturnType() Վերադարձնում է մեթոդի վերադարձվող տեսակն:
getGenericReturnType() Վերադարձնում է մեթոդի վերադարձվող տեսակն հաշվի առնելով գեներացված մեթոդները:
getParameterTypes() Վերադարձնում է մեթոդի պարամետրերի զանգվածը:
getGenericParameterTypes() Վերադարձնում է մեթոդի պարամետրերի զանգվածը հաշվի առնելով գեներացված մեթոդները:
getExceptionTypes() Վերադարձնում է բացառությունները, որոնք կարող է դուրս գալ մոտոդը:
getGenericExceptionTypes() Վերադարձնում է բացառությունները, որոնք կարող է դուրս գալ մոտոդը, հաշվի առնելով գեներացված պարամետրերը:
getAnnotations() Վերադարձնում է մեթոդի անոտացիաները, ներառյալ ծնողական անոտացիաները:
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);
}

Մեր main մեթոդը:


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

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

Մեր արդյունքը:

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

Մեր բոլոր մոդիֆիկատորներն ունեն public մուտք, ուստի վերջին մեթոդը մեզ վերադարձնում է միավորներ: Եթե մենք մեր կոդը փոփոխենք, ապա կտեսնենք մեր մոդիֆիկատորները:


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

Օգտագործելով այս մեթոդը, մենք կարող ենք ստանալ մեթոդի վերադարձվող տիպը:


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

Ավելացնենք մեր Person կլասի մեջ մեթոդ, որը վերադարձնում է գեներացված տիպ, և փորձենք ստանալ նրա վերադարձվող արժեքը:


public List<String> someMethod() {
    //շատ օգտակար և կարևոր մեթոդ
    return null;
}

Եվ փոփոխենք մեր հիմնական մեթոդը:


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

Մեր մեթոդի արդյունքը:

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

Մեթոդներ getParameterTypes() և getGenericParameterTypes()

Շարունակենք փոփոխել մեր Person կլասի մեթոդը և ավելացնենք ևս երկու մեթոդներ:


public List<String> someMethod(List<String> list, String s) {
    //շատ օգտակար և կարևոր մեթոդ
    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);
}

Եվ մեր կոդի արդյունքներում մենք կտեսնենք տարբերությունները և արդյունքները, որոնք հենց նրանք վերադարձնում են:

interface java.util.List
class java.lang.String

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

Մեթոդներ getExceptionTypes() և getGenericExceptionTypes()

Օգտագործելով այս մեթոդները մենք կարող ենք ստանալ բացառությունների զանգված, որոնք կարող է արտանետել մեր մեթոդը, ինչպես նաև գեներացված պարամետրերով (եթե այդպիսիք կան) բացառություններ: Մենք վերցնենք մի նոր օրինակ՝ թաքնված ստատիկ դասով:


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

    private void process() throws IOException {}
}

Կմտնենք այն մեթոդներից մեկի մեջ, ահա այսպես՝ Process:


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

Հիանալի, այսպես տեսնում ենք մեր բացառությունը:

[class java.io.IOException]

Հիմա մեր բոլորի մոտ դնում է նորով `կակա կոնակը. Մենք այն տեղադրում ենք մեր հիմնական դասում:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Եվ Main: միջաշինքի կոդզ՝ այստեղ արդեն առկա է փոփոխություն:


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]
class java.io.IOException

Մեթոդներ getAnnotations() և getDeclaredAnnotations()

Շարունակե՛նք աշխատել մեր նոր դասի հետ և ավելացնենք երկու անոտացիաներ: Ստեղծենք մեր սեփական Annotation անոտացիան:


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

Մեր դասի՝ Main:


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

Արդյունքում կստանանք այսպիսի արդյունք պատուհանի վրա:

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

Այսպիսով մենք կարող ենք ստանալ մեր մեթոդների անոտացիաները, իսկ getAnnotations մեթոդի միջոցով մենք ստանում ենք հասանելիություն նաև ծնողական դասի անոտացիաներին:

Այսօր մենք ծանոթացանք մեթոդների և դաշտերի հետ ռեֆլեքսիայի միջոցով և տեսանք, թե ինչ տվյալներ կարող ենք ստանալ նրանց միջոցով:

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ