java.lang.reflect.Field-Klasse

Die Field- Klasse bietet Informationen über und dynamischen Zugriff auf ein einzelnes Feld einer Klasse oder Schnittstelle. Field ermöglicht auch eine erweiternde Typkonvertierung während eines Get- oder Set-Zugriffsvorgangs, löst jedoch eine IllegalArgumentException aus , wenn eine Einschränkung auftreten würde.

Um ein Field- Objekt zu erhalten, schreiben wir zunächst eine 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;
}

Und hier ist unser Code für die Arbeit mit dieser 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);
    }
}

So erhalten wir die Liste der Felder unserer Klasse, mit denen wir später arbeiten werden. Hier ist das Ergebnis:

[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 statisches finales int com.company.Person.MAX_AGE, öffentliches statisches finales int com.company.Person.MIN_AGE]

Lassen Sie uns nun herausfinden, was wir mit diesen Daten machen können. Lassen Sie uns über die Methoden der Field- Klasse sprechen:

Methode Beschreibung
getType() Gibt ein Klassenobjekt zurück, das den deklarierten Typ des dadurch dargestellten Felds identifiziertFeldObjekt.
getAnnotatedType() Gibt eine zurückAnnotatedTypeObjekt, das die Verwendung eines Typs darstellt, um den deklarierten Typ des durch dieses Feld dargestellten Felds anzugeben.
getGenericType() Gibt a zurückTypObjekt, das den deklarierten Typ des dadurch dargestellten Felds darstelltFeldObjekt.
getName() Gibt den Namen des dadurch dargestellten Feldes zurückFeldObjekt.
getModifiers() Gibt einen int-Wert zurück, der die Java-Sprachmodifikatoren für das dadurch dargestellte Feld kodiertFeldObjekt.
getAnnotations() Gibt die Anmerkungen für dieses Feld zurück. Wenn keine Anmerkungen vorhanden sind, wird ein leeres Array zurückgegeben.

getType()-, getName()- und getModifiers()-Methoden

Wir können die Methode getType() verwenden , um den Typ unseres Feldes abzurufen. Schreiben wir eine Methode:


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

Und wir erhalten dieses Ergebnis:

Klasse java.lang.String
int
boolesche
Klasse java.lang.String
int
int

Jetzt fügen wir unserer Klasse eine Methode hinzu, um den Namen eines Feldes zu erhalten. Dies erleichtert die Navigation durch die Felder unserer Klasse.


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

Jetzt ist das Ergebnis verständlicher:

Feldtyp – Klasse java.lang.String
Feldname – Name

Feldtyp – int
Feldname – Alter

Feldtyp – boolean
Feldname – isMale

Feldtyp – Klasse java.lang.String
Feldname – Adresse

Feldtyp – int
Feldname – MAX_AGE

Feldtyp – int
Feldname – MIN_AGE

Großartig! Lassen Sie uns unsere Methode noch etwas modifizieren! Wir werden hier Zugriffsmodifikatoren hinzufügen


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

Und schauen wir uns an, was e.getModifiers() zurückgibt. Diese Methode gibt einen int zurück , mit dem wir den Status der Zugriffsmodifikatoren unseres Felds bestimmen können. Die Modifier- Klasse enthält statische Variablen, die für jeden spezifischen Modifikator des Felds verantwortlich sind.

Verpacken wir unseren Rückgabewert in Modifier.toString() und erhalten seinen Wert sofort als Text:

Feldtyp – Klasse java.lang.String
Feldname – Name
Modifikatoren – privat

Feldtyp – int
Feldname – Alter
Modifikatoren – privat

Feldtyp – boolean
Feldname – isMale
Modifikatoren – öffentlich

Feldtyp – Klasse java.lang.String
Feldname – Adresse
Modifikatoren – geschützt

Feldtyp – int
Feldname – MAX_AGE
Modifikatoren – öffentliches statisches Finale

Feldtyp – int
Feldname – MIN_AGE
Modifikatoren – öffentliches statisches Finale

So sieht es ohne Modifier.toString() aus :

Feldtyp – Klasse java.lang.String
Feldname – Name
Modifikatoren – 2

Feldtyp – int
Feldname – Alter
Modifikatoren – 2

Feldtyp – boolean
Feldname – isMale
Modifikatoren – 1

Feldtyp – Klasse java.lang.String
Feldname – Adresse
Modifikatoren – 4

Feldtyp – int
Feldname – MAX_AGE
Modifikatoren – 25

Feldtyp – int
Feldname – MIN_AGE
Modifikatoren – 25

Methoden getAnnotations(), getAnnotatedType() und getGenericType()

Ändern wir die Person- Klasse, um mit diesen Methoden zu arbeiten. Wir schreiben unsere eigene Anmerkung und wenden sie auf unsere Felder an. Und wir werden noch ein paar weitere Felder hinzufügen.

Lassen Sie uns zwei Anmerkungen erstellen. Wir werden den Variablennamen in Pig Latin an einen übergeben und den zweiten für Elemente verwenden:


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

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

Und wir werden unsere Hauptklasse und die Personenklasse ändern :


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

Es ist an der Zeit, sich die Ergebnisse unserer Methoden anzusehen und herauszufinden, wozu sie dienen:

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="\u0055\u0073\u0065\u0072\u0020\u006e\u0069\u0063 \u006b\u006e\u0061\u006d\u0065\u0073")]
[@Name(name="\u0041\u006d\u0065\u002d\u006e\u0061\u0079")] [
]
  • getAnnotatedType gibt die Anmerkung für das angegebene Feld zurück, falls vorhanden. Wir erhalten die Anmerkung für das Feld und können es perfekt sehen.

  • Mit getGenericType können Sie einen parametrisierten Typ korrekt anzeigen.

  • getAnnotations gibt die auf unser Objekt angewendeten Anmerkungen zurück.

Auf diese Weise können wir ganz einfach alle Daten zu jedem Feld in unserer Klasse sowie seine Zugriffsmodifikatoren, Anmerkungen und Datentypen abrufen.

java.lang.reflect.Method-Klasse

Super! Wir haben über die Bereiche unserer Klasse gesprochen. Jetzt ist es an der Zeit, über die Methoden zu sprechen.

Um ein Methodenobjekt abzurufen , rufen wir die Methode getMethod auf und übergeben ihr den Namen unserer Methode. Dies ist der grundlegende Weg, um ein Methodenobjekt abzurufen :


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

Wir werden weiterhin mit unserer Klasse zusammenarbeiten. Fügen wir Getter und Setter sowie die Methoden hashCode, equal und toString hinzu :


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

Bereiten wir nun eine Reihe von Methoden vor, die wir mithilfe der Method- Klasse untersuchen . Hier ist eine Liste der wichtigsten Methoden:

Methode Beschreibung
getName() Gibt den Namen der Methode zurück.
getModifiers() Gibt den Zugriffsmodifikator dieser Methode zurück.
getReturnType() Gibt den Rückgabetyp der Methode zurück.
getGenericReturnType() Gibt den Rückgabetyp der Methode zurück und berücksichtigt dabei generische Methoden.
getParameterTypes() Gibt ein Array von Methodenparametern zurück.
getGenericParameterTypes() Gibt ein Array von Methodenparametern zurück, die generische Methoden berücksichtigen.
getExceptionTypes() Gibt die Ausnahmen zurück, die die Methode auslösen kann.
getGenericExceptionTypes() Gibt die Ausnahmen zurück, die die Methode unter Berücksichtigung parametrisierter Typen auslösen kann.
getAnnotations() Gibt die Anmerkungen für die Methode zurück, einschließlich übergeordneter Anmerkungen.
getDeclaredAnnotations() Gibt die Anmerkungen für die Methode zurück, mit Ausnahme der übergeordneten Anmerkungen.

Um ein Array der Methoden unserer Klasse zu erhalten, können wir diese Methode aufrufen:


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

Es wird uns alle Methoden in unserer Klasse zur Verfügung stellen.

getName()- und getModifiers()-Methoden

Wir können getName verwenden , um den Namen jeder Methode abzurufen:


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

Um nun die Modifikatoren zu erhalten, schreiben wir eine Methode, die getModifiers verwendet :


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

Hier ist unsere Hauptmethode :


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

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

Unser Ergebnis:

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

Alle unsere Methoden verfügen über den öffentlichen Modifikator, daher gibt die letzte Methode ein Array von Einsen zurück. Wenn wir unseren Code ändern, sehen wir die Modifikatoren selbst:


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

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

getReturnedType()

Mit dieser Methode können wir den Rückgabetyp der Methode ermitteln:


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

getGenericReturnType()

Geben wir unserer Person- Klasse eine Methode, die den in einen parametrisierten Typ eingeschlossenen Typ zurückgibt, und versuchen, seinen Rückgabewert abzurufen:


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

Und wir werden unsere Hauptmethode aktualisieren:


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

Ergebnis unserer Methode:

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

Methoden getParameterTypes() und getGenericParameterTypes()

Wir modifizieren unsere Person- Klasse weiter und fügen zwei weitere Methoden hinzu:


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

Mit dem ersten erhalten wir die Parameter unserer Methoden und mit dem zweiten erhalten wir auch parametrisierte 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);
        }
}

Wir werden nur auf eine unserer Methoden zugreifen. Um über einen bestimmten Namen auf eine Methode zuzugreifen, rufen wir getMethod auf und übergeben den Namen und die Parameter der gewünschten Methode:


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

Wenn wir unseren Code ausführen, werden wir sehen, wie sich die Methoden unterscheiden und was sie zurückgeben:

Schnittstelle java.util.List
Klasse java.lang.String

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

getExceptionTypes()- und getGenericExceptionTypes()-Methoden

Wir können diese Methoden verwenden, um ein Array von Ausnahmen abzurufen, die unsere Methode auslösen kann, sowie Ausnahmen mit parametrisierten Typen (falls vorhanden). Lassen Sie uns ein neues Beispiel verwenden, das eine versteckte statische Klasse hat:


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

    private void process() throws IOException {}
}

Und wir rufen Methoden für unsere Prozessorklasse auf :


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

Jetzt können wir unsere Ausnahme sehen:

[Klasse java.io.IOException]

Lassen Sie uns nun den Typ parametrisieren. Wir werden unsere Hauptklasse ändern:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Und der Code der Hauptmethode :


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

Innerhalb dieser Methode erhalten wir ein TypeVariables- Objekt, das eine generische übergeordnete Schnittstelle für Typvariablen ist. Und darin können wir nun den internen Parameter abrufen, nämlich unsere verschachtelte Ausnahme:

[E]
Klasse java.io.IOException

Methoden getAnnotations() und getDeclaredAnnotations()

Lassen Sie uns diese neue Klasse weiterhin verwenden und ihr einige Anmerkungen hinzufügen. Wir erstellen unsere eigene Annotation -Annotation:


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Und wenden Sie es auf unsere Methode an:


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

}

Und natürlich fügen wir eine Methode hinzu, um alle unsere Anmerkungen anzuzeigen:


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

Implementierung unserer Hauptmethode :


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

Die resultierende Bildschirmausgabe ist:

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

Auf diese Weise können wir die Annotationen abrufen, die auf unsere Methoden angewendet wurden, und mit der Methode getAnnotations können wir auch auf die übergeordneten Annotationen der Klasse zugreifen.

Heute haben wir erfahren, wie Reflexion mit Methoden und Feldern funktionieren kann und welche Daten wir damit gewinnen können!