java.lang.reflect.Fältklass

Fältklassen tillhandahåller information om och dynamisk åtkomst till ett enda fält i en klass eller gränssnitt . Fält tillåter också en breddningstypkonvertering under en get- eller set-åtkomstoperation, men kastar ett IllegalArgumentException om avsmalning skulle inträffa.

För att få ett Field- objekt skriver vi först en klass:


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

Och här är vår kod för att arbeta 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);
    }
}

Så här får vi listan över fälten i vår klass, som vi kommer att arbeta med senare. Här är resultatet:

[privat java.lang.String com.company.Person.name, private int com.company.Person.age, public boolean com.company.Person.isMan, skyddad 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]

Låt oss nu ta reda på vad vi kan göra med dessa data. Låt oss prata om metoderna för fältklassen :

Metod Beskrivning
getType() Returnerar ett klassobjekt som identifierar den deklarerade typen av fältet som representeras av dettaFältobjekt.
getAnnotatedType() Returnerar enAnnotatedTypeobjekt som representerar användningen av en typ för att specificera den deklarerade typen av fältet som representeras av detta fält.
getGenericType() Returnerar aTypobjekt som representerar den deklarerade typen av fältet som representeras av dettaFältobjekt.
hämta namn() Returnerar namnet på fältet som representeras av dettaFältobjekt.
getModifiers() Returnerar en int som kodar Java-språkmodifierarna för fältet som representeras av dettaFältobjekt.
getAnnotations() Returnerar kommentarerna för detta fält. Om det inte finns några anteckningar returnerar det en tom array.

getType(), getName() och getModifiers() metoder

Vi kan använda metoden getType() för att få typen av vårt fält. Låt oss skriva en metod:


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

Och vi får detta resultat:

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

Låt oss nu lägga till en metod till vår klass för att få namnet på ett fält. Detta kommer att göra det lättare att navigera i fälten i vår klass.


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 är resultatet mer förståeligt:

Fälttyp - klass java.lang.String
Fältnamn - namn

Fälttyp - int
Fältnamn - ålder

Fälttyp - boolean
Fältnamn - isMale

Fälttyp - klass java.lang.String
Fältnamn - adress

Fälttyp - int
Fältnamn - MAX_AGE

Fälttyp - int
Fältnamn - MIN_AGE

Bra! Låt oss ändra vår metod lite mer! Vi lägger till åtkomstmodifierare här


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

Och låt oss titta på vad e.getModifiers() returnerar. Denna metod returnerar en int som låter oss bestämma tillståndet för vårt fälts åtkomstmodifierare. Klassen Modifier innehåller statiska variabler som är ansvariga för varje specifik modifierare av fältet.

Låt oss slå in vårt returvärde i Modifier.toString() och omedelbart få dess värde som text:

Fälttyp - klass java.lang.String
Fältnamn - namn
Modifierare - privat

Fälttyp - int
Fältnamn - ålder
Modifierare - privat

Fälttyp - boolean
Fältnamn - isMale
Modifierare - offentlig

Fälttyp - klass java.lang.String
Fältnamn - adress
Modifierare - skyddad

Fälttyp - int
Fältnamn - MAX_AGE
Modifierare - public static final

Fälttyp - int
Fältnamn - MIN_AGE
Modifierare - public static final

Så här ser det ut utan Modifier.toString() :

Fälttyp - klass java.lang.String
Fältnamn - namn
Modifierare - 2

Fälttyp - int
Fältnamn - ålder
Modifierare - 2

Fälttyp - boolean
Fältnamn - isMale
Modifierare - 1

Fälttyp - klass java.lang.String
Fältnamn - adressmodifierare
- 4

Fälttyp - int
Fältnamn - MAX_AGE
Modifierare - 25

Fälttyp - int
Fältnamn - MIN_AGE
Modifierare - 25

getAnnotations(), getAnnotatedType() och getGenericType() metoder

Låt oss ändra klassen Person så att den fungerar med dessa metoder. Vi kommer att skriva vår egen anteckning och tillämpa den på våra fält. Och vi kommer att lägga till några fler fält.

Låt oss skapa två kommentarer. Vi kommer att skicka variabelnamnet på Pig Latin till en, och vi kommer att använda den andra för element:


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

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

Och vi kommer att ändra vår huvudklass och personklassen :


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 är dags att titta på resultaten av våra metoder och ta reda på vad de är till för:

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 returnerar anteckningen för det givna fältet, om någon. Vi får anteckningen för fältet och vi kan se den perfekt.

  • getGenericType låter dig visa en parametrerad typ korrekt.

  • getAnnotations returnerar anteckningarna som tillämpas på vårt objekt.

Så här kan vi enkelt få all data om varje fält i vår klass, såväl som dess åtkomstmodifierare, annoteringar och datatyper.

java.lang.reflect.Method class

Super! Vi har pratat om fälten i vår klass. Nu är det dags att prata om metoderna.

För att få ett Method- objekt anropar vi metoden getMethod och ger det namnet på vår metod. Detta är det grundläggande sättet att få ett metodobjekt :


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

Vi kommer att fortsätta arbeta med vår klass. Låt oss lägga till getters och setters, och hashCode, equals och 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);
    }
}

Låt oss nu förbereda en uppsättning metoder som vi kommer att undersöka med klassen Method . Här är en lista över de viktigaste metoderna:

Metod Beskrivning
hämta namn() Returnerar namnet på metoden.
getModifiers() Returnerar åtkomstmodifieraren för denna metod.
getReturnType() Returnerar metodens returtyp.
getGenericReturnType() Returnerar returtypen för metoden, med hänsyn till generiska metoder.
getParameterTypes() Returnerar en matris med metodparametrar.
getGenericParameterTypes() Returnerar en uppsättning metodparametrar som tar hänsyn till generiska metoder.
getExceptionTypes() Returnerar undantagen som metoden kan ge.
getGenericExceptionTypes() Returnerar undantagen som metoden kan skapa, och tar hänsyn till parametriserade typer.
getAnnotations() Returnerar annoteringarna för metoden, inklusive överordnade kommentarer.
getDeclaredAnnotations() Returnerar annoteringarna för metoden, exklusive överordnade kommentarer.

För att få en uppsättning av metoderna för vår klassvilja kan vi kalla denna metod:


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

Det kommer att ge oss alla metoder i vår klass.

metoderna getName() och getModifiers().

Vi kan använda getName för att få namnet på varje metod:


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

Nu för att få modifierarna, låt oss skriva en metod som använder getModifiers :


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

Här är vår huvudsakliga metod:


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

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

Vårt resultat:

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

Alla våra metoder har den offentliga modifieraren, så den sista metoden returnerar en rad ettor. Om vi ​​modifierar vår kod kommer vi att se själva modifierarna:


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

Den här metoden låter oss få metodens returtyp:


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

Låt oss ge vår Person- klass en metod som returnerar typen insvept i en parameteriserad typ, och försöka få dess returvärde:


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

Och vi kommer att uppdatera vår huvudmetod:


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

Resultatet av vår metod:

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>

metoderna getParameterTypes() och getGenericParameterTypes().

Vi fortsätter att modifiera vår Person- klass och lägger till ytterligare två metoder:


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

Den första kommer att låta oss få parametrarna för våra metoder, och den andra kommer att ge oss parametriserade typer också.


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 kommer bara åt en av våra metoder. För att komma åt en metod med ett specifikt namn anropar vi getMethod och skickar in namnet och parametrarna för metoden vi vill ha:


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 kör vår kod kommer vi att se hur metoderna skiljer sig och vad de returnerar:

gränssnitt java.util.List
klass java.lang.String

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

metoderna getExceptionTypes() och getGenericExceptionTypes().

Vi kan använda dessa metoder för att få en rad undantag som vår metod kan ge, såväl som undantag med parametriserade typer (om några). Låt oss använda ett nytt exempel som har en dold statisk klass:


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

    private void process() throws IOException {}
}

Och vi kallar metoder på vår processorklass :


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 kan vi se vårt undantag:

[klass java.io.IOException]

Låt oss nu parametrisera typen. Vi kommer att ändra vår huvudklass:


private static class Processor<E extends IOException> {

    private void process() throws E {
    }
}

Och koden för huvudmetoden :


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

Inuti den här metoden fick vi ett TypeVariables- objekt, som är ett generiskt överordnat gränssnitt för typvariabler. Och inuti det kan vi nu få den interna parametern, nämligen vårt kapslade undantag:

[E]
klass java.io.IOException

metoderna getAnnotations() och getDeclaredAnnotations().

Låt oss fortsätta använda den här nya klassen och lägga till ett par kommentarer till den. Vi skapar vår egen anteckningsanteckning :


@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {

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

Och tillämpa det på vår metod:


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

}

Och naturligtvis lägger vi till en metod för att visa alla våra kommentarer:


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 huvudmetod :


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

Den resulterande skärmutgången är:

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

Så här kan vi få de anteckningar som har tillämpats på våra metoder, och metoden getAnnotations låter oss komma åt klassens överordnade kommentarer också.

Idag har vi bekantat oss med hur reflektion kan fungera med metoder och fält, och vilken data vi kan få med det!