- Получение данных с помощью рефлексии
- Методы getType(), getName(), getModifiers()
- Методы getAnnotations(), getAnnotatedType(), getGenericType()
- Класс java.lang.reflect.Method
- Методы getName() и getModifiers()
- getReturnedType()
- getGenericReturnType()
- Методы getParameterTypes() и getGenericParameterTypes()
- Методы getExceptionTypes() и getGenericExceptionTypes()
- Методы getAnnotations() и getDeclaredAnnotations()
Получение данных с помощью рефлексии
Класс Field предоставляет информацию и динамический доступ к одному полю класса или интерфейса. Field также разрешает расширение преобразований во время операции get или set access, но выдает исключение IllegalArgumentException , если происходит сужение преобразования.
Чтобы получить класс Filed , мы напишем класс, с которым будем работать, а так же обработчик для этого:
public class Main < public static void main(String[] args) < Field[] fields = Person.class.getDeclaredFields(); ListactualFields = getFieldNames(fields); System.out.println(actualFields); > static List getFieldNames(Field[] fields) < return List.of(fields); >>
Таким образом мы получаем список полей нашего класса, с которыми будем дальше работать. Результат выполнения выглядит так:
[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]Теперь давай разбираться, что мы можем сделать с эти набором данных. Поговорим о методах класса Field :
Метод | Описание |
---|---|
getType() | Возвращает объект класса, который определяет объявленный тип поля, представленного этим объектом Field . |
getAnnotatedType() | Возвращает объект AnnotatedType , который представляет использование типа для указания объявленного типа поля, представленного этим полем. |
getGenericType() | Возвращает объект Type , который представляет объявленный тип поля, представленного этим объектом Field . |
getName() | Возвращает имя поля, представленного этим объектом Field . |
getModifiers() | Возвращает модификаторы языка Java для поля, представленного этим объектом Field , в виде целого числа. |
getAnnotations() | Возвращает аннотации этого поля. Если аннотаций нет — пустой массив. |
Методы getType(), getName(), getModifiers()
С помощью метода getType() мы можем получить тип нашего поля. Напишем метод:
static void printTypes(List fields) < fields.forEach(e ->System.out.println(e.getType())); >
И получим такой результат:
И давай сразу добавим в наш класс метод для получения имени поля. Так будет проще ориентироваться по полям нашего класса.
static void printTypesAndNames(List fields) < fields.forEach(e ->System.out.printf("Field type - %s\nField name - %s\n\n", e.getType(), e.getName())); >
Получим результат, уже более понятный для пользователя:
Field type — class java.lang.String
Field name — name
Field type — int
Field name — age
Field type — boolean
Field name — isMale
Field type — class java.lang.String
Field name — address
Field type — int
Field name — MAX_AGE
Field type — int
Field name — MIN_AGE
Отлично, давай еще модифицировать наш метод! Добавим сюда модификаторы доступа:
static void printFieldInfo(List 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()))); >
И давай разберем, что возвращает e.getModifiers() . Этот метод возвращает число int , внутри которого мы можем определить модификаторы доступа нашего поля. Внутри класса Modifier лежат статические переменные, которые отвечают за определенный модификатор поля.
Обернем наше возвращаемое значение в Мodifier.toString() — и сразу получим значение в текстовом виде:
Field type — class java.lang.String
Field name — name
Modifiers — private
Field type — int
Field name — age
Modifiers — private
Field type — boolean
Field name — isMale
Modifiers — public
Field type — class java.lang.String
Field name — address
Modifiers — protected
Field type — int
Field name — MAX_AGE
Modifiers — public static final
Field type — int
Field name — MIN_AGE
Modifiers — public static final
И вот так это выглядит без Мodifier.toString() :
Field type — class java.lang.String
Field name — name
Modifiers — 2
Field type — int
Field name — age
Modifiers — 2
Field type — boolean
Field name — isMale
Modifiers — 1
Field type — class java.lang.String
Field name — address
Modifiers — 4
Field type — int
Field name — MAX_AGE
Modifiers — 25
Field type — int
Field name — MIN_AGE
Modifiers — 25
Методы getAnnotations(), getAnnotatedType(), getGenericType()
Давай модифицируем класс Person для работы с текущими методами. Мы напишем свою аннотацию, которую добавим к нашим полям, и добавим еще немного полей.
Создадим две аннотации. В одну будем передавать имя переменной на русском, а вторую будем использовать для элементов:
@Target(value=ElementType.FIELD) @Retention(value= RetentionPolicy.RUNTIME) public @interface Name
@Target(< ElementType.TYPE_USE >) @Retention(RetentionPolicy.RUNTIME) public @interface Number
И изменим наш основной класс и класс Person :
public class Person < @Name(name = "Имя") private String name; @Name(name = "Никнэймы пользователя") Listnicknames; private final Class type; private int @Number[] number; public Person(Class type) < this.type = type; >>
public static void main(String[] args) < Field[] fields = Person.class.getDeclaredFields(); ListactualFields = getFieldNames(fields); printAdditionalInfo(actualFields); > static void printAdditionalInfo(List 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()))); >
Самое время посмотреть на результат наших методов и разобрать еще раз их назначение:
getGenericType:
java.lang.Class
java.util.List
class java.lang.String
class [I
getAnnotations:
[]
[@Name(name=»\u041d\u0438\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f»)]
[@Name(name=»\u0418\u043c\u044f»)]
[]
- getAnnotatedType возвращает аннотацию для данного поля, если такая есть. У нас есть аннотация для поля и мы отлично ее видим.
- getGenericType позволяет корректно отображать дженерализованные параметры.
- getAnnotations возвращает аннотации, которые стоят над нашим объектом.
Таким образом мы можем легко получить все данные о каждом поле в нашем классе, о его модификаторах доступа, аннотациях и типах данных.
Класс java.lang.reflect.Method
Супер, мы поговорили о полях нашего класса, пора поговорить о методах.
Чтобы получить объект класса Method , мы вызовем getMethod и передадим туда имя нашего метода. Это базовый способ для получения класса Method :
Method getNameMethod = Person.class.getMethod("getName");
Будем продолжать работать с нашим классом. Добавим геттеры и сеттеры, хэш-код, equals и 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'; > @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); >>
И давай подготовим набор методов, которые будем смотреть у класса Method . Вот список основных методов:
Метод | Описание |
---|---|
getName() | Возвращает имя метода. |
getModifiers() | Возвращает модификатор доступа этого метода. |
getReturnType() | Возвращает возвращаемый тип метода. |
getGenericReturnType() | Возвращает возвращаемый тип метода с учетом дженерализированных методов. |
getParameterTypes() | Возвращает массив параметров метода. |
getGenericParameterTypes() | Возвращает массив параметров метода с учетом дженерализированных методов. |
getExceptionTypes() | Возвращает исключения, которые может выбросить метод. |
getGenericExceptionTypes() | Возвращает исключения, которые может выбросить метод, с учетом дженерализированных параметров. |
getAnnotations() | Возвращает аннотации для метода, включая родительские аннотации. |
getDeclaredAnnotations() | Возвращает аннотации для метода, игнорируя родительские аннотации. |
Чтобы получить массив методов, которые вернет нам наш класс, мы можем вызвать такой метод:
Method[] methods = Person.class.getDeclaredMethods();
C его помощью мы получим все методы в нашем классе.
Методы getName() и getModifiers()
Чтобы получить название всех методов, мы можем воспользоваться getName :
static List getMethodsName(Method[] methods)
И чтобы получить модификаторы, напишем метод с использованием getModifiers :
static List getModifiers(Method[] methods)
public static void main(String[] args)[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Все наши модификаторы имеют доступ public , поэтому последний метод возвращает нам единицы. Если мы модифицируем наш код, то увидим наши модификаторы:
public static void main(String[] args)[getName, equals, toString, hashCode, setName, getAddress, isMale, getAge, setAge, setMale, setAddress]
[public, public, public, public, public, public, public, public, public, public, public]
getReturnedType()
C помощью этого метода мы можем получить возвращаемый тип метода:
static void getReturnedType(Method[] methods)
class java.lang.String
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void
getGenericReturnType()
Добавим в наш класс Person метод, который возвращает обернутый в дженерик тип, и попробуем получить его возвращаемое значение:
И модифицируем наш основной метод:
static void getGenericReturnType(Method[] methods)
class java.lang.String
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void
java.util.List
Методы getParameterTypes() и getGenericParameterTypes()
Продолжаем модифицировать наш метод класса Person и добавим еще два метода:
public List someMethod(List list, String s) < //очень полезный и важный метод return null; >
Первый позволит нам получить параметры наших методов, а второй выдаст еще и дженерализированые параметры.
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); >>
Будем обращаться только к одному нашему методу. Чтобы обратится к методу по определенному названию, вызовем getMethod и передадим туда название и параметры нужного нам метода:
public static void main(String[] args) throws NoSuchMethodException
И по результатам нашего кода увидим, чем отличаются методы и что они возвращают:
interface java.util.List
class java.lang.String
java.util.List
class java.lang.String
Методы getExceptionTypes() и getGenericExceptionTypes()
C помощью этих методов можно получить массив исключений, которые может выбросить наш метод, а еще исключения, обернутые в дженерики (если они есть). Возьмем новый пример со скрытым статическим классом:
private static class Processor < private void init() <>private void process() throws IOException <> >
И будем вызывать методы у нашего класса Process :
public static void main(String. args) throws NoSuchMethodException
В результате отлично видно наше исключение:
Теперь обернем это все в дженерик. Модифицируем наш основной класс:
private static class Processor < private void process() throws E < >>
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); >> > >
Внутри этого метода мы получили TypeVariables — это общий супер-интерфейс для переменных типа. А внутри его уже мы можем получить внутренний параметр, а именно наше вложенной исключение:
Методы getAnnotations() и getDeclaredAnnotations()
Продолжим работать с нашим новым классом и добавим в него парочку аннотаций. Создаем собственную аннотацию Annotation :
@Retention(RetentionPolicy.RUNTIME) @interface Annotation
И добавляем ее к нашему методу:
@Annotation(key = "key", value = "value") private void process() throws E
И, конечно, метод, чтобы отобразить все наши аннотации:
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(); >>
Реализация нашего Main класса:
public static void main(String. args)
Получим такой результат на экране:
Таким образом мы можем получить аннотации, которые принадлежат нашим методам, а с помощью метода getAnnotations мы получаем доступ также и к родительским аннотациям класса.
Сегодня мы с тобой познакомились с тем, как работают методы и поля с рефлексией и какие данные мы можем получить с ее помощью!