java.lang.reflect.Field-klasse

De klasse Field biedt informatie over en dynamische toegang tot een enkel veld van een klasse of interface. Field staat ook een verbredingstypeconversie toe tijdens een get- of set-toegangsbewerking, maar genereert een IllegalArgumentException als vernauwing zou optreden.

Om een ​​Field- object te krijgen, schrijven we eerst een klasse:


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

En hier is onze code voor het werken met die klasse:


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

Zo krijgen we de lijst met velden van onze klasse, waarmee we later zullen werken. Dit is het resultaat:

[private java.lang.String com.company.Person.name, private int com.company.Person.age, public boolean com.company.Person.isMale, beschermd java.lang.String com.company.Person.address, public statische finale int com.company.Person.MAX_AGE, openbare statische finale int com.company.Person.MIN_AGE]

Laten we nu eens kijken wat we met deze gegevens kunnen doen. Laten we het hebben over de methoden van de klasse Field :

Methode Beschrijving
getType() Retourneert een Class-object dat het gedeclareerde type identificeert van het veld dat hierdoor wordt vertegenwoordigdVeldvoorwerp.
getAnnotatedType() Retourneert eengeannoteerd typeobject dat het gebruik van een type vertegenwoordigt om het gedeclareerde type van het veld dat door dit veld wordt vertegenwoordigd, te specificeren.
getGenericType() Retourneert eenTypeobject dat het gedeclareerde type vertegenwoordigt van het veld dat hierdoor wordt vertegenwoordigdVeldvoorwerp.
getNaam() Retourneert de naam van het veld dat hierdoor wordt vertegenwoordigdVeldvoorwerp.
getModifiers() Retourneert een int die de Java-taalmodificatoren codeert voor het veld dat hierdoor wordt vertegenwoordigdVeldvoorwerp.
getAnnotaties() Retourneert de annotaties voor dit veld. Als er geen annotaties zijn, wordt een lege matrix geretourneerd.

methoden getType(), getName() en getModifiers().

We kunnen de getType() methode gebruiken om het type van ons veld te krijgen. Laten we een methode schrijven:


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

En we krijgen dit resultaat:

klasse java.lang.String
int
booleaanse
klasse java.lang.String
int
int

Laten we nu een methode aan onze klasse toevoegen om de naam van een veld te krijgen. Dit maakt het gemakkelijker om door de velden van onze klas te navigeren.


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

Nu is het resultaat begrijpelijker:

Veldtype - klasse java.lang.String
Veldnaam - naam

Veldtype - int
Veldnaam - leeftijd

Veldtype - boolean
Veldnaam - isMale

Veldtype - klasse java.lang.String
Veldnaam - adres

Veldtype - int
Veldnaam - MAX_AGE

Veldtype - int
Veldnaam - MIN_AGE

Geweldig! Laten we onze methode nog wat aanpassen! We zullen hier toegangsmodifiers toevoegen


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

En laten we eens kijken naar wat e.getModifiers() retourneert. Deze methode retourneert een int waarmee we de status van de toegangsmodifiers van ons veld kunnen bepalen. De klasse Modifier bevat statische variabelen die verantwoordelijk zijn voor elke specifieke modifier van het veld.

Laten we onze retourwaarde in Modifier.toString() verpakken en de waarde onmiddellijk als tekst ophalen:

Veldtype - class java.lang.String
Veldnaam - name
Modifiers - private

Veldtype - int
Veldnaam - leeftijd
Modifiers - private

Veldtype - boolean
Veldnaam - isMale
Modifiers - public

Veldtype - class java.lang.String
Veldnaam - adres
Modifiers - beschermd

Veldtype - int
Veldnaam - MAX_AGE
Modifiers - openbare statische finale

Veldtype - int
Veldnaam - MIN_AGE
Modifiers - openbare statische finale

Zo ziet het eruit zonder Modifier.toString() :

Veldtype - class java.lang.String
Veldnaam - naam
Modifiers - 2

Veldtype - int
Veldnaam - leeftijd
Modifiers - 2

Veldtype - boolean
Veldnaam - isMale
Modifiers - 1

Veldtype - class java.lang.String
Veldnaam - adres
Modifiers - 4

Veldtype - int
Veldnaam - MAX_AGE
Modifiers - 25

Veldtype - int
Veldnaam - MIN_AGE
Modifiers - 25

getAnnotations(), getAnnotatedType() en getGenericType() methoden

Laten we de Person- klasse aanpassen om met deze methoden te werken. We schrijven onze eigen annotatie en passen deze toe op onze velden. En we zullen nog een paar velden toevoegen.

Laten we twee annotaties maken. We zullen de variabelenaam in Pig Latin doorgeven aan één, en we zullen de tweede gebruiken voor elementen:


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

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

En we zullen onze hoofdklasse en de persoonsklasse wijzigen :


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

Het is tijd om naar de resultaten van onze methoden te kijken en erachter te komen waar ze voor zijn:

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

krijgAnnotaties:
[]
[@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 retourneert de annotatie voor het opgegeven veld, indien aanwezig. We krijgen de annotatie voor het veld en we kunnen het perfect zien.

  • Met getGenericType kunt u een geparametriseerd type correct weergeven.

  • getAnnotations retourneert de annotaties die op ons object zijn toegepast.

Dit is hoe we gemakkelijk alle gegevens over elk veld in onze klasse kunnen krijgen, evenals de toegangsmodificatoren, annotaties en gegevenstypen.

java.lang.reflect.Method klasse

Super! We hebben het gehad over de velden van onze klas. Nu is het tijd om over de methoden te praten.

Om een ​​Method- object te krijgen, noemen we de methode getMethod en geven we de naam van onze methode door. Dit is de basismanier om een ​​Method- object te krijgen:


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

We blijven werken met onze klas. Laten we getters en setters, en hashCode, equals en toString methoden toevoegen:


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

Laten we nu een set methoden voorbereiden die we zullen onderzoeken met behulp van de klasse Method . Hier is een lijst met de belangrijkste methoden:

Methode Beschrijving
getNaam() Retourneert de naam van de methode.
getModifiers() Retourneert de toegangsmodifier van deze methode.
getReturnType() Retourneert het retourtype van de methode.
getGenericReturnType() Retourneert het retourtype van de methode, rekening houdend met generieke methoden.
getParameterTypes() Retourneert een array met methodeparameters.
getGenericParameterTypes() Retourneert een array van methodeparameters, rekening houdend met generieke methoden.
getExceptionTypes() Retourneert de uitzonderingen die de methode kan genereren.
getGenericExceptionTypes() Retourneert de uitzonderingen die de methode kan genereren, rekening houdend met geparameteriseerde typen.
getAnnotaties() Retourneert de annotaties voor de methode, inclusief bovenliggende annotaties.
getDeclaredAnnotations() Retourneert de annotaties voor de methode, exclusief bovenliggende annotaties.

Om een ​​array van de methoden van onze klasse will te krijgen, kunnen we deze methode noemen:


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

Het geeft ons alle methoden in onze klas.

methoden getName() en getModifiers().

We kunnen getName gebruiken om de naam van elke methode te krijgen:


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

Om de modifiers te krijgen, gaan we een methode schrijven die getModifiers gebruikt :


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

Dit is onze belangrijkste methode:


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

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

Ons resultaat:

[getName, is gelijk aan, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Al onze methoden hebben de public modifier, dus de laatste methode retourneert een reeks enen. Als we onze code wijzigen, zien we onze modifiers zelf:


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

    System.out.println(getMethodsName(methods));
    System.out.println(modifyModifiers(getModifiers(methods)));
}
[getName, is gelijk aan, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress] [
public, public, public, public, public, public, public, public, public, public, public]

getReturnedType()

Met deze methode kunnen we het retourtype van de methode krijgen:


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

Laten we onze Person- klasse een methode geven die het type retourneert dat is verpakt in een geparametriseerd type, en proberen de retourwaarde te krijgen:


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

En we zullen onze hoofdmethode bijwerken:


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

Resultaat van onze werkwijze:

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>

methoden getParameterTypes() en getGenericParameterTypes().

We blijven onze Person- klasse aanpassen en voegen nog twee methoden toe:


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

Met de eerste kunnen we de parameters van onze methoden ophalen, en met de tweede krijgen we ook geparametriseerde typen.


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

We hebben alleen toegang tot een van onze methoden. Om toegang te krijgen tot een methode met een specifieke naam, bellen we getMethod en geven we de naam en parameters door van de methode die we willen:


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

Als we onze code uitvoeren, zullen we zien hoe de methoden verschillen en wat ze retourneren:

interface java.util.List
klasse java.lang.String

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

methoden getExceptionTypes() en getGenericExceptionTypes().

We kunnen deze methoden gebruiken om een ​​reeks uitzonderingen te krijgen die onze methode kan genereren, evenals uitzonderingen met geparametriseerde typen (indien aanwezig). Laten we een nieuw voorbeeld gebruiken met een verborgen statische klasse:


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

    private void process() throws IOException {}
}

En we zullen methoden aanroepen in onze Processor- klasse:


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

Nu kunnen we onze uitzondering zien:

[klasse java.io.IOException]

Laten we nu het type parametriseren. We zullen onze hoofdklasse aanpassen:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

En de code van de hoofdmethode :


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

Binnen deze methode hebben we een TypeVariables- object, een generieke bovenliggende interface voor typevariabelen. En daarbinnen kunnen we nu de interne parameter krijgen, namelijk onze geneste uitzondering:

[E]
klasse java.io.IOException

getAnnotations() en getDeclaredAnnotations() methoden

Laten we deze nieuwe klasse blijven gebruiken en er een paar annotaties aan toevoegen. We zullen onze eigen annotatie- annotatie maken:


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

En pas het toe op onze methode:


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

}

En natuurlijk voegen we een methode toe om al onze annotaties weer te geven:


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

Implementatie van onze hoofdmethode :


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

De resulterende schermuitvoer is:

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

Dit is hoe we de annotaties kunnen krijgen die op onze methoden zijn toegepast, en de methode getAnnotations geeft ons ook toegang tot de bovenliggende annotaties van de klasse.

Vandaag hebben we kennis gemaakt met hoe reflectie kan werken met methoden en velden, en welke data we er mee kunnen krijgen!