CodeGym /Curs Java /Modulul 2: Java Core /Obținerea de date folosind reflectarea

Obținerea de date folosind reflectarea

Modulul 2: Java Core
Nivel , Lecţie
Disponibil

java.lang.reflect.Clasa de câmp

Clasa Field oferă informații despre și acces dinamic la un singur câmp al unei clase sau interfețe. Field permite, de asemenea, o conversie de tip lărgire în timpul unei operațiuni de obținere sau setare de acces, dar aruncă o excepție IllegalArgumentException dacă ar avea loc o îngustare.

Pentru a obține un obiect Field , vom scrie mai întâi o clasă:


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

Și iată codul nostru pentru a lucra cu acea clasă:


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

Așa obținem lista câmpurilor clasei noastre, cu care vom lucra mai târziu. Iată rezultatul:

[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]

Acum să ne dăm seama ce putem face cu aceste date. Să vorbim despre metodele clasei Field :

Metodă Descriere
getType() Returnează un obiect Class care identifică tipul declarat al câmpului reprezentat de acestaCampobiect.
getAnnotatedType() Returnează unAnnotatedTypeobiect care reprezintă utilizarea unui tip pentru a specifica tipul declarat al câmpului reprezentat de acest Câmp.
getGenericType() Returnează aTipobiect care reprezintă tipul declarat al câmpului reprezentat de acestaCampobiect.
getName() Returnează numele câmpului reprezentat de aceastaCampobiect.
getModifiers() Returnează un int care codifică modificatorii limbajului Java pentru câmpul reprezentat de acestaCampobiect.
getAdnotations() Returnează adnotările pentru acest câmp. Dacă nu există adnotări, returnează o matrice goală.

Metodele getType(), getName() și getModifiers().

Putem folosi metoda getType() pentru a obține tipul câmpului nostru. Să scriem o metodă:


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

Și obținem acest rezultat:

clasa java.lang.String
int clasa
booleană
java.lang.String
int
int

Acum să adăugăm o metodă la clasa noastră pentru a obține numele unui câmp. Acest lucru va facilita navigarea în câmpurile clasei noastre.


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

Acum rezultatul este mai de înțeles:

Tip câmp - clasa java.lang.String
Nume câmp - nume

Tip câmp - int
Nume câmp - vârstă

Tip câmp - boolean
Nume câmp - isMale

Tip câmp - clasă java.lang.String
Nume câmp - adresă

Tip câmp - int
Nume câmp - MAX_AGE

Tip câmp - int
Nume câmp - MIN_AGE

Grozav! Să ne mai modificăm metoda! Vom adăuga modificatori de acces aici


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

Și să ne uităm la ce returnează e.getModifiers() . Această metodă returnează un int care ne permite să determinăm starea modificatorilor de acces ai câmpului nostru. Clasa Modifier conține variabile statice responsabile pentru fiecare modificator specific al câmpului.

Să încapsulăm valoarea noastră returnată în Modifier.toString() și să obținem imediat valoarea acesteia ca text:

Tip câmp - clasa java.lang.String
Nume câmp - nume
Modificatori - private

Tip câmp - int
Nume câmp - vârstă
Modificatori - private

Tip câmp - boolean
Nume câmp - isMale
Modificatori - public

Tip câmp - clasa java.lang.String
Nume câmp - adresa
Modificatori - protejat

Tip câmp - int
Nume câmp - MAX_AGE
Modificatori - public static final

Tip câmp - int
Nume câmp - MIN_AGE
Modificatori - public static final

Iată cum arată fără Modifier.toString() :

Tip câmp - clasa java.lang.String
Nume câmp - nume
Modificatori - 2

Tip câmp - int
Nume câmp - vârstă
Modificatori - 2

Tip câmp - boolean
Nume câmp - isMale
Modificatori - 1

Tip câmp - clasă java.lang.String
Nume câmp - Modificatori de adresă
- 4

Tip câmp - int
Nume câmp - MAX_AGE
Modificatori - 25

Tip câmp - int
Nume câmp - MIN_AGE
Modificatori - 25

Metodele getAnnotations(), getAnnotatedType() și getGenericType().

Să modificăm clasa Person pentru a lucra cu aceste metode. Vom scrie propria noastră adnotare și o vom aplica câmpurilor noastre. Și vom mai adăuga câteva câmpuri.

Să creăm două adnotări. Vom trece numele variabilei în Pig Latin unuia și îl vom folosi pe al doilea pentru elemente:


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

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

Și ne vom schimba clasa principală și clasa Persoană :


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

Este timpul să ne uităm la rezultatele metodelor noastre și să ne dăm seama pentru ce sunt acestea:

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

getGenericType:
java.lang.Class<java.lang. Obiect>
java.util.List<java.lang.String>
clasa java.lang.String
clasa [I

getAdnotations:
[]
[@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 returnează adnotarea pentru câmpul dat, dacă există. Primim adnotarea pentru teren și o vedem perfect.

  • getGenericType vă permite să afișați corect un tip parametrizat.

  • getAnnotations returnează adnotările aplicate obiectului nostru.

Acesta este modul în care putem obține cu ușurință toate datele despre fiecare câmp din clasa noastră, precum și modificatorii de acces, adnotările și tipurile de date ale acestuia.

java.lang.reflect.Clasa metoda

Super! Am vorbit despre domeniile clasei noastre. Acum este timpul să vorbim despre metode.

Pentru a obține un obiect Method , apelăm metoda getMethod , dându-i numele metodei noastre. Aceasta este modalitatea de bază de a obține un obiect Method :


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

Vom continua să lucrăm cu clasa noastră. Să adăugăm getters și setters și metodele hashCode, equals și 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);
    }
}

Acum să pregătim un set de metode pe care le vom examina folosind clasa Method . Iată o listă cu cele mai importante metode:

Metodă Descriere
getName() Returnează numele metodei.
getModifiers() Returnează modificatorul de acces al acestei metode.
getReturnType() Returnează tipul de returnare al metodei.
getGenericReturnType() Returnează tipul de returnare al metodei, luând în considerare metodele generice.
getParameterTypes() Returnează o matrice de parametri ai metodei.
getGenericParameterTypes() Returnează o serie de parametri ai metodei, luând în considerare metodele generice.
getExceptionTypes() Returnează excepțiile pe care metoda le poate arunca.
getGenericExceptionTypes() Returnează excepțiile pe care metoda le poate arunca, ținând cont de tipurile parametrizate.
getAdnotations() Returnează adnotările pentru metodă, inclusiv adnotările părinte.
getDeclaredAnnotations() Returnează adnotările pentru metodă, excluzând adnotările părinte.

Pentru a obține o serie de metode ale clasei noastre va, putem numi această metodă:


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

Ne va oferi toate metodele din clasa noastră.

metodele getName() și getModifiers().

Putem folosi getName pentru a obține numele fiecărei metode:


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

Acum, pentru a obține modificatorii, să scriem o metodă care folosește getModifiers :


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

Iată metoda noastră principală :


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

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

Rezultatul nostru:

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

Toate metodele noastre au modificatorul public , astfel încât ultima metodă returnează o serie de unele. Dacă ne modificăm codul, vom vedea înșiși modificatorii:


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

Această metodă ne permite să obținem tipul de returnare al metodei:


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

Să dăm clasei noastre Person o metodă care returnează tipul împachetat într-un tip parametrizat și să încercăm să obținem valoarea returnată:


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

Și vom actualiza metoda noastră principală:


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

Rezultatul metodei noastre:

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>

Metodele getParameterTypes() și getGenericParameterTypes().

Continuăm să modificăm clasa Person , adăugând încă două metode:


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

Primul ne va permite să obținem parametrii metodelor noastre, iar al doilea ne va oferi și tipuri parametrizate.


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

Vom accesa doar una dintre metodele noastre. Pentru a accesa o metodă cu un anumit nume, apelăm getMethod și trecem numele și parametrii metodei pe care o dorim:


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

Rulând codul nostru, vom vedea cum diferă metodele și ce returnează:

interfață java.util.List
clasa java.lang.String

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

Metodele getExceptionTypes() și getGenericExceptionTypes().

Putem folosi aceste metode pentru a obține o serie de excepții pe care metoda noastră le poate arunca, precum și excepții cu tipuri parametrizate (dacă există). Să folosim un nou exemplu care are o clasă statică ascunsă:


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

    private void process() throws IOException {}
}

Și vom apela metode din clasa noastră Procesor :


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

Acum putem vedea excepția noastră:

[clasa java.io.IOException]

Acum să parametrizăm tipul. Vom modifica clasa noastră principală:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Și codul metodei principale :


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

În această metodă, avem un obiect TypeVariables , care este o interfață părinte generică pentru variabilele de tip. Și în interiorul acestuia, acum putem obține parametrul intern, și anume excepția noastră imbricată:


Clasa [E] java.io.IOException

Metodele getAnnotations() și getDeclaredAnnotations().

Să continuăm să folosim această nouă clasă și să îi adăugăm câteva adnotări. Vom crea propria noastră adnotare Adnotare:


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Și aplicați-l la metoda noastră:


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

}

Și, desigur, vom adăuga o metodă pentru a afișa toate adnotările noastre:


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

Implementarea metodei noastre principale :


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

Rezultatul ecranului rezultat este:

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

Acesta este modul în care putem obține adnotările care au fost aplicate metodelor noastre, iar metoda getAnnotations ne permite să accesăm și adnotările părinte ale clasei.

Astăzi ne-am familiarizat cu modul în care reflecția poate funcționa cu metode și domenii și ce date putem obține cu ea!

Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION