java.lang.reflect.Feltklasse

Felt - klassen gir informasjon om og dynamisk tilgang til et enkelt felt i en klasse eller et grensesnitt. Feltet tillater også en utvidelsestypekonvertering under en hent eller sett tilgangsoperasjon, men kaster et IllegalArgumentException hvis innsnevring ville oppstå.

For å få et feltobjekt , skriver vi først en 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;
}

Og her er koden vår for å jobbe med den klassen:


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

Slik får vi listen over feltene til klassen vår, som vi skal jobbe med senere. Her er resultatet:

[privat java.lang.String com.company.Person.name, private int com.company.Person.age, public boolean com.company.Person.isMann, beskyttet java.lang.String com.company.Person.address, public static final int com.company.Person.MAX_AGE, offentlig static final int com.company.Person.MIN_AGE]

La oss nå finne ut hva vi kan gjøre med disse dataene. La oss snakke om metodene til Field -klassen:

Metode Beskrivelse
getType() Returnerer et klasseobjekt som identifiserer den deklarerte typen av feltet representert av detteFeltgjenstand.
getAnnotatedType() Returnerer enAnnotatedTypeobjekt som representerer bruken av en type for å spesifisere den deklarerte typen av feltet representert av dette feltet.
getGenericType() Returnerer enTypeobjekt som representerer den deklarerte typen av feltet representert av detteFeltgjenstand.
getName() Returnerer navnet på feltet representert av detteFeltgjenstand.
getModifiers() Returnerer en int som koder Java-språkmodifikatorene for feltet representert av detteFeltgjenstand.
getAnnotations() Returnerer merknadene for dette feltet. Hvis det ikke er noen merknader, returnerer den en tom matrise.

getType(), getName() og getModifiers()-metoder

Vi kan bruke getType() -metoden for å få feltets type. La oss skrive en metode:


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

Og vi får dette resultatet:

class java.lang.String
int
boolean
class java.lang.String
int
int

La oss nå legge til en metode i klassen vår for å få navnet på et felt. Dette vil gjøre det lettere å navigere i feltene til klassen vår.


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

Nå er resultatet mer forståelig:

Felttype - klasse java.lang.String
Feltnavn - navn

Felttype - int
Feltnavn - alder

Felttype - boolsk
Feltnavn - isMale

Felttype - klasse java.lang.String
Feltnavn - adresse

Felttype - int
Feltnavn - MAX_AGE

Felttype - int
Feltnavn - MIN_AGE

Flott! La oss endre metoden vår litt mer! Vi legger til tilgangsmodifikatorer her


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

Og la oss se på hva e.getModifiers() returnerer. Denne metoden returnerer en int som lar oss bestemme tilstanden feltets tilgangsmodifikatorer. Modifier - klassen inneholder statiske variabler som er ansvarlige for hver spesifikke modifikator i feltet.

La oss pakke returverdien vår i Modifier.toString() og umiddelbart få verdien som tekst:

Felttype - klasse java.lang.String
Feltnavn - navn
Modifikatorer - privat

Felttype - int
Feltnavn - alder
Modifikatorer - privat

Felttype - boolesk
Feltnavn - isMale
Modifikatorer - offentlig

Felttype - klasse java.lang.String
Feltnavn - adresse
Modifikatorer - beskyttet

Felttype - int
Feltnavn - MAX_AGE
Modifikatorer - offentlig statisk endelig

Felttype - int
Feltnavn - MIN_AGE
Modifikatorer - offentlig statisk endelig

Slik ser det ut uten Modifier.toString() :

Felttype - klasse java.lang.String
Feltnavn - navn
Modifikatorer - 2

Felttype - int
Feltnavn - alder
Modifikatorer - 2

Felttype - boolesk
Feltnavn - isMale
Modifikatorer - 1

Felttype - klasse java.lang.String
Feltnavn - adresse
Modifikatorer - 4

Felttype - int
Feltnavn - MAX_AGE
Modifikatorer - 25

Felttype - int
Feltnavn - MIN_AGE
Modifikatorer - 25

getAnnotations(), getAnnotatedType() og getGenericType() metoder

La oss endre Person -klassen slik at den fungerer med disse metodene. Vi skriver vår egen merknad og bruker den på feltene våre. Og vi legger til noen flere felt.

La oss lage to merknader. Vi vil gi variabelnavnet på Pig Latin til en, og vi vil bruke den andre for elementer:


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

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

Og vi vil endre hovedklassen og Person- klassen:


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

Det er på tide å se på resultatene av metodene våre og finne ut hva de er til for:

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 returnerer merknaden for det gitte feltet, hvis noen. Vi får merknaden for feltet og vi kan se den perfekt.

  • getGenericType lar deg vise en parameterisert type korrekt.

  • getAnnotations returnerer merknadene brukt på objektet vårt.

Dette er hvordan vi enkelt kan få all data om hvert felt i klassen vår, så vel som tilgangsmodifikatorer, merknader og datatyper.

java.lang.reflect.Metode klasse

Super! Vi har snakket om feltene i klassen vår. Nå er det på tide å snakke om metodene.

For å få et Method- objekt kaller vi getMethod- metoden og gir det navnet på metoden vår. Dette er den grunnleggende måten å få et metodeobjekt på :


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

Vi vil fortsette å jobbe med klassen vår. La oss legge til gettere og settere, og hashCode, equals og toString -metoder:


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

La oss nå forberede et sett med metoder som vi vil undersøke ved å bruke metodeklassen . Her er en liste over de viktigste metodene:

Metode Beskrivelse
getName() Returnerer navnet på metoden.
getModifiers() Returnerer tilgangsmodifikatoren for denne metoden.
getReturnType() Returnerer returtypen til metoden.
getGenericReturnType() Returnerer returtypen til metoden, og tar hensyn til generiske metoder.
getParameterTypes() Returnerer en rekke metodeparametere.
getGenericParameterTypes() Returnerer en rekke metodeparametere som tar hensyn til generiske metoder.
getExceptionTypes() Returnerer unntakene som metoden kan kaste.
getGenericExceptionTypes() Returnerer unntakene som metoden kan gi, og tar hensyn til parameteriserte typer.
getAnnotations() Returnerer merknadene for metoden, inkludert overordnede merknader.
getDeclaredAnnotations() Returnerer merknadene for metoden, unntatt overordnede merknader.

For å få en rekke av metodene til klassen vår kan vi kalle denne metoden:


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

Det vil gi oss alle metodene i klassen vår.

getName() og getModifiers() metoder

Vi kan bruke getName for å få navnet på hver metode:


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

Nå for å få modifikatorene, la oss skrive en metode som bruker getModifiers :


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

Her er hovedmetoden vår :


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

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

Resultatet vårt:

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

Alle metodene våre har den offentlige modifikatoren, så den siste metoden returnerer en rekke ener. Hvis vi endrer koden vår, vil vi se selve modifikatorene:


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, public]

getReturnedType()

Denne metoden lar oss få returtypen til metoden:


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

La oss gi vår Person- klasse en metode som returnerer typen pakket inn i en parameterisert type, og prøve å få returverdien:


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

Og vi oppdaterer hovedmetoden vår:


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

Resultatet av metoden vår:

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>

getParameterTypes() og getGenericParameterTypes() metoder

Vi fortsetter å endre Person -klassen vår, og legger til ytterligere to metoder:


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

Den første vil la oss få parametrene til metodene våre, og den andre vil også gi oss parameteriserte typer.


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

Vi vil bare få tilgang til én av metodene våre. For å få tilgang til en metode med et spesifikt navn, kaller vi getMethod og sender inn navnet og parameterne til metoden vi ønsker:


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

Når vi kjører koden vår, vil vi se hvordan metodene er forskjellige og hva de returnerer:

grensesnitt java.util.List
class java.lang.String

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

getExceptionTypes()- og getGenericExceptionTypes()-metodene

Vi kan bruke disse metodene for å få en rekke unntak som metoden vår kan gi, så vel som unntak med parameteriserte typer (hvis noen). La oss bruke et nytt eksempel som har en skjult statisk klasse:


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

    private void process() throws IOException {}
}

Og vi kaller metoder på prosessorklassen vår :


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

Nå kan vi se unntaket vårt:

[klasse java.io.IOException]

La oss nå parameterisere typen. Vi vil endre hovedklassen vår:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Og koden til hovedmetoden :


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

Inne i denne metoden fikk vi et TypeVariables- objekt, som er et generisk overordnet grensesnitt for typevariabler. Og inne i det kan vi nå få den interne parameteren, nemlig vårt nestede unntak:

[E]
klasse java.io.IOException

getAnnotations()- og getDeclaredAnnotations()-metodene

La oss fortsette å bruke denne nye klassen og legge til et par merknader til den. Vi lager vår egen kommentarkommentar :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Og bruk det på metoden vår:


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

}

Og selvfølgelig legger vi til en metode for å vise alle kommentarene våre:


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

Implementering av vår hovedmetode :


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

Den resulterende skjermutgangen er:

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

Dette er hvordan vi kan få merknadene som er brukt på metodene våre, og getAnnotations- metoden lar oss også få tilgang til de overordnede merknadene til klassen.

I dag ble vi kjent med hvordan refleksjon kan fungere med metoder og felt, og hvilke data vi kan få med det!