klasa java.lang.reflect.Field

Klasa Field zapewnia informacje i dynamiczny dostęp do pojedynczego pola klasy lub interfejsu. Pole zezwala również na rozszerzanie transformacji podczas operacji pobierania lub ustawiania dostępu, ale zgłasza wyjątek IllegalArgumentException , jeśli wystąpi zawężanie transformacji.

Aby uzyskać klasę Filed , napiszemy klasę, z którą będziemy pracować, a także program obsługi do tego:


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 sam prowadzący:


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

Otrzymujemy w ten sposób listę dziedzin naszej klasy, z którymi będziemy dalej pracować. Wynik wykonania wygląda następująco:

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

Teraz zastanówmy się, co możemy zrobić z tym zestawem danych. Porozmawiajmy o metodach klasy Field :

metoda Opis
getType() Zwraca obiekt klasy, która definiuje zadeklarowany typ pola reprezentowanego przez ten obiektpole.
getAnnotatedType() Zwraca obiektTyp adnotacjiA, który reprezentuje użycie typu do określenia zadeklarowanego typu pola reprezentowanego przez to pole.
getGenericType() Zwraca obiekttypA, która reprezentuje zadeklarowany typ pola reprezentowanego przez ten obiektpole.
pobierzNazwę() Zwraca nazwę pola reprezentowanego przez ten obiektpole.
getModifiers() Zwraca modyfikatory języka Java dla pola reprezentowanego przez ten obiektpole, jako liczba całkowita.
getAnnotations() Zwraca adnotacje dla tego pola. Jeśli nie ma adnotacji, pusta tablica.

Metody getType(), getName(), getModifiers()

Za pomocą metody getType() możemy uzyskać typ naszego pola. Napiszmy metodę:


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

I otrzymujemy taki wynik:

klasa java.lang.String
int
logiczna
klasa java.lang.String
int
int

I od razu dodajmy metodę do naszej klasy, aby uzyskać nazwę pola. Ułatwi to poruszanie się po polach naszej klasy.


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

Otrzymujemy wynik, który jest już bardziej zrozumiały dla użytkownika:

Typ pola - klasa java.lang.String
Nazwa pola - nazwa

Typ pola - int
Nazwa pola - wiek

Typ pola - boolean
Nazwa pola - isMale

Typ pola - klasa java.lang.String
Nazwa pola - adres

Typ pola - int
Nazwa pola - MAX_AGE

Typ pola - int
Nazwa pola - MIN_AGE

Świetnie, zmodyfikujmy jeszcze trochę naszą metodę! Dodajmy tutaj modyfikatory dostępu:


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

Zobaczmy, co zwraca funkcja e.getModifiers() . Ta metoda zwraca int , w ramach którego możemy zdefiniować modyfikatory dostępu do naszego pola. Wewnątrz klasy Modifier znajdują się zmienne statyczne, które odpowiadają za określony modyfikator pola.

Zapakujmy naszą zwracaną wartość w Modifier.toString() - i natychmiast pobierzmy wartość w postaci tekstowej:

Typ pola - class java.lang.String
Nazwa pola - name
Modyfikatory - private

Typ pola - int
Nazwa pola - age
Modyfikatory - private

Typ pola - boolean
Nazwa pola - isMale
Modyfikatory - public

Typ pola - class java.lang.String
Nazwa pola - adres
Modyfikatory - protected

Typ pola - int
Nazwa pola - MAX_AGE
Modyfikatory - public static final

Typ pola - int
Nazwa pola - MIN_AGE
Modyfikatory - public static final

A tak to wygląda bez Modifier.toString() :

Typ pola - class java.lang.String
Nazwa pola - nazwa
Modyfikatory - 2

Typ pola - int
Nazwa pola - wiek
Modyfikatory - 2

Typ pola - boolean
Nazwa pola - isMale
Modyfikatory - 1

Typ pola - class java.lang.String
Nazwa pola - adres
Modyfikatory - 4

Typ pola - int
Nazwa pola - MAX_AGE
Modyfikatory - 25

Typ pola - int
Nazwa pola - MIN_AGE
Modyfikatory - 25

metody getAnnotations(), getAnnotatedType(), getGenericType().

Zmodyfikujmy klasę Person , aby działała z bieżącymi metodami. Napiszemy własną adnotację, którą dodamy do naszych pól i dodamy jeszcze kilka pól.

Utwórzmy dwie adnotacje. Nazwę zmiennej w języku rosyjskim przekażemy do jednego, a drugiego użyjemy dla elementów:


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

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

I zmień naszą główną klasę i klasę 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())));
}

Czas przyjrzeć się wynikom naszych metod i ponownie przeanalizować ich przeznaczenie:

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

getGenericType:
java.lang.Class<java.lang. Obiekt>
java.util.List<java.lang.String> klasa
klasa java.lang.String
[otrzymuję

adnotacje:
[]
[@Nazwa(nazwa="\u041d\u0438\u043a\u0438 \u043f\u043e\u043b\u044c \u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f")]
[@Nazwa(nazwa="\u0418\u043c\u044f")]
[]
  • getAnnotatedType zwraca adnotację dla danego pola, jeśli istnieje. Mamy adnotację dla pola i doskonale ją widzimy.

  • getGenericType umożliwia poprawne wyświetlanie ogólnych parametrów.

  • getAnnotations zwraca adnotacje, które znajdują się na górze naszego obiektu.

W ten sposób możemy łatwo uzyskać wszystkie dane o każdym polu w naszej klasie, jego modyfikatorach dostępu, adnotacjach i typach danych.

Klasa java.lang.reflect.Method

Świetnie, omówiliśmy dziedziny naszej klasy, czas porozmawiać o metodach.

Aby otrzymać obiekt klasy Method , wywołujemy getMethod i przekazujemy nazwę naszej metody. Oto podstawowy sposób uzyskania klasy Method :


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

Będziemy dalej pracować z naszą klasą. Dodajmy gettery i settery, hash code, 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);
    }
}

I przygotujmy zestaw metod, którym przyjrzymy się klasie Method . Oto lista głównych metod:

metoda Opis
pobierzNazwę() Zwraca nazwę metody.
getModifiers() Zwraca modyfikator dostępu tej metody.
getReturnType() Zwraca zwracany typ metody.
getGenericReturnType() Zwraca zwracany typ metody, biorąc pod uwagę uogólnione metody.
getParameterTypes() Zwraca tablicę parametrów metody.
getGenericParameterTypes() Zwraca tablicę parametrów metody, w tym metody uogólnione.
getExceptionType() Zwraca wyjątki, które metoda może zgłosić.
getGenericExceptionTypes() Zwraca wyjątki, które metoda może zgłosić, biorąc pod uwagę ogólne parametry.
getAnnotations() Zwraca adnotacje dla metody, w tym adnotacje nadrzędne.
getDeclaredAnnotations() Zwraca adnotacje dla metody, ignorując adnotacje nadrzędne.

Aby uzyskać tablicę metod, które zwróci nam nasza klasa, możemy wywołać tę metodę:


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

Dzięki niemu otrzymamy wszystkie metody w naszej klasie.

metody getName() i getModifiers().

Aby uzyskać nazwy wszystkich metod, możemy użyć getName :


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

Aby uzyskać modyfikatory, napiszmy metodę używającą getModifiers :


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

Nasza główna metoda :


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

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

Nasz wynik:

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

Wszystkie nasze modyfikatory mają publiczny dostęp , więc ostatnia metoda zwraca nam jednostki. Jeśli zmodyfikujemy nasz kod, zobaczymy nasze modyfikatory:


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

getReturnedType()

Za pomocą tej metody możemy uzyskać zwracany typ metody:


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

getGenericReturnType()

Dodajmy metodę do naszej klasy Person , która zwraca typ ogólny i spróbujmy uzyskać jej wartość zwracaną:


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

I zmodyfikuj naszą główną metodę:


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

Wynik naszej metody:

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>

metody getParameterTypes() i getGenericParameterTypes().

Kontynuujemy modyfikację naszej metody klasy Person i dodajemy jeszcze dwie metody:


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

Pierwszy pozwoli nam uzyskać parametry naszych metod, a drugi da nam również parametry uogólnione.


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

Odniesiemy się tylko do jednej z naszych metod. Aby uzyskać dostęp do metody o określonej nazwie, wywołaj getMethod i podaj nazwę oraz parametry metody, której potrzebujemy:


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

I zgodnie z wynikami naszego kodu zobaczymy, czym różnią się metody i co zwracają:

interfejs java.util.List
klasa java.lang.String

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

metody getExceptionTypes() i getGenericExceptionTypes().

Korzystając z tych metod, możesz uzyskać tablicę wyjątków, które nasza metoda może zgłaszać, a także wyjątki opakowane w generyczne (jeśli istnieją). Weźmy nowy przykład z ukrytą klasą statyczną:


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

    private void process() throws IOException {}
}

I będziemy wywoływać metody w naszej klasie 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));
}

W rezultacie nasz wyjątek jest wyraźnie widoczny:

[klasa java.io.IOException]

Teraz zawińmy to wszystko w ogólny. Zmodyfikujmy naszą główną klasę:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

I kod dla klasy 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);
            }
        }
    }
}

Wewnątrz tej metody mamy TypeVariables - jest to wspólny superinterfejs dla zmiennych typu. A w nim możemy już uzyskać wewnętrzny parametr, a mianowicie nasz zagnieżdżony wyjątek:

[E]
klasa java.io.IOException

metody getAnnotations() i getDeclaredAnnotations().

Kontynuujmy naszą nową klasę i dodajmy do niej kilka adnotacji. Stwórzmy własną adnotację :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

I dodaj to do naszej metody:


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

}

I oczywiście metoda wyświetlania wszystkich naszych adnotacji:


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

Implementacja naszej klasy Main :


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

Na ekranie otrzymujemy następujący wynik:

proces
[@com.company.Main&Annotation(klucz=”klucz”, wartość=”wartość”)]

W ten sposób możemy uzyskać adnotacje należące do naszych metod, a przy pomocy metody getAnnotations uzyskujemy również dostęp do adnotacji nadrzędnych klasy.

Dziś poznaliśmy jak metody i pola działają z refleksją i jakie dane możemy dzięki temu uzyskać!