CodeGym /Java курс /Модул 2: Java Core /Получаване на данни чрез отражение

Получаване на данни чрез отражение

Модул 2: Java Core
Ниво , Урок
На разположение

java.lang.reflect.Field клас

Класът Field предоставя информация за и динамичен достъп до едно поле на клас or интерфейс. Field също позволява преобразуване на разширяващ се тип по време на операция за получаване or задаване на достъп, но хвърля IllegalArgumentException , ако се получи стесняване.

За да получим Field обект, първо ще напишем клас:


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

И ето нашия code за работа с този клас:


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]

Сега нека разберем Howво можем да направим с тези данни. Нека поговорим за методите на класа Field :

Метод Описание
getType() Връща обект Class, който идентифицира декларирания тип на полето, представено от thisПолеобект.
getAnnotatedType() Връща anAnnotatedTypeобект, който представлява използването на тип за указване на декларирания тип на полето, представено от това поле.
getGenericType() Връща aТипобект, който представлява декларирания тип на полето, представено от thisПолеобект.
getName() Връща името на полето, представено от товаПолеобект.
getModifiers() Връща int, codeиращ модификаторите на езика Java за полето, представено от товаПолеобект.
getAnnotations() Връща анотациите за това поле. Ако няма анотации, връща празен масив.

методи 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
Име на поле - възраст

Тип поле - boolean
Име на поле - isMale

Тип поле - клас java.lang.String
Име на поле - address

Тип поле - 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())));
}

И нека да разгледаме Howво връща e.getModifiers() . Този метод връща int , което ни позволява да определим състоянието на модификаторите за достъп на нашето поле. Класът Modifier съдържа статични променливи, отговорни за всеки конкретен модификатор на полето.

Нека увием нашата върната стойност в Modifier.toString() и веднага да получим нейната стойност като текст:

Тип поле - клас java.lang.String
Име на поле - име
Модификатори - private

Тип поле - int
Име на поле - възраст
Модификатори - частно

Тип поле - boolean
Име на поле - isMale
Модификатори - public

Тип поле - клас java.lang.String
Име на поле - address
Модификатори - защитено

Тип поле - int
Име на поле - MAX_AGE
Модификатори - публичен статичен финал

Тип поле - int
Име на поле - MIN_AGE
Модификатори - публичен статичен финал

Ето How изглежда без Modifier.toString() :

Тип поле - клас java.lang.String
Име на поле - име
Модификатори - 2

Тип поле - int
Име на поле - възраст
Модификатори - 2

Тип поле - boolean
Име на поле - isMale
Модификатори - 1

Тип поле - клас 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 = "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())));
}

Време е да разгледаме резултатите от нашите методи и да разберем за Howво служат:

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
клас [I

getAnnotations:
[]
[@Name(name="\u0055\u0073\u0065\u0072\u0020\u006e\u0069\u0063 \u006b\u006e\u0061\u006d\u0065\u0073")]
[@Име(име="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [
]
  • getAnnotatedType връща анотацията за даденото поле, ако има такава. Получаваме анотацията за полето и я виждаме перфектно.

  • getGenericType ви позволява да показвате правилно параметризиран тип.

  • getAnnotations връща анотациите, приложени към нашия обект.

Ето How можем лесно да получим всички данни за всяко поле в нашия клас, Howто и неговите модификатори за достъп, анотации и типове данни.

клас java.lang.reflect.Method

Супер! Говорихме за полетата на нашия клас. Сега е време да поговорим за методите.

За да получим обект Method , извикваме метода getMethod , като му предаваме името на нашия метод. Това е основният начин за получаване на обект Method :


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

Ще продължим да работим с нашия клас. Нека добавим гетери и сетери, Howто и методи 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);
}

Ето нашия основен метод:


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]

Всички наши методи имат публичен модификатор, така че последният метод връща масив от единици. Ако променим codeа си, ще видим самите модификатори:


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
boolean
клас java.lang.String
int
void
клас java.lang.String
boolean
int
void
void
void

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
boolean
клас 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) {
    // 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);
}

Изпълнявайки нашия code, ще видим How се различават методите и Howво връщат:

интерфейс java.util.List
клас java.lang.String

java.util.List<java.lang.String>
клас java.lang.String

методи getExceptionTypes() и getGenericExceptionTypes().

Можем да използваме тези методи, за да получим масив от изключения, които нашият метод може да хвърли, Howто и изключения с параметризирани типове (ако има такива). Нека използваме нов пример, който има скрит статичен клас:


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

И codeът на основния метод:


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="ключ", value="стойност")]

Това е начинът, по който можем да получим анотациите, които са бor приложени към нашите методи, а методът getAnnotations ни позволява да получим достъп и до родителските анотации на класа.

Днес се запознахме с това How отражението може да работи с методи и полета и Howви данни можем да получим с него!

Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION