Для исследования типов в программе кроме класса Class язык Java предоставляет еще ряд классов, которые определены главным образом в пакете java.lang.reflect:
Field: описывает поле класса
Method: описывает метод типа
Constructor: описывает конструктор класса
Для получения этих компонентов класса применяются соответственно следующие методы класса Class:
Field[] getFields() / getDeclaredFields(): возвращает поля класса
Method[] getMethods() / getDeclaredMethods(): возвращает методы класса
Constructor[] getConstructors() / getDeclaredConstructors(): возвращает конструкторы класса
Методы getFields(), getMethods() и getConstructors() возвращают массивы общедоступных полей, методов и конструкторов (определенных с модификатором public),
поддерживаемых классом, в том числе и поля и методы, унаследованные от суперклассов. Методы getDeclaredFields(), getDeclaredMethods() и
getDeclaredConstructors() возвращают массивы из всех полей, методов и конструкторов, объявленных непосредственно в классе, в том числе и закрытые, приватные компоненты. Но эти массивы НЕ включают компоненты суперклассов.
Таким образом, в зависимости от уровня доступа компонентов а также от того, хотим мы получить также компоненты суперклассов или хотим ограничиться компонентами, которые определены непосредственно в классе, зависит выбор конкретного метода для получения компонентов.
Допустим, у нас есть следующая иерархия классов:
class Person {
private String name;
private int age;
public String getName() {return name;}
public int getAge() {return age;}
public Person(){
this.name = "Undefined";
this.age = 18;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
Исследуем тип Person:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// получаем класс для класса Person
Class personClass = Person.class;
Field[] fields = personClass.getFields();
Method[] methods = personClass.getMethods();
Constructor[] ctors = personClass.getConstructors();
System.out.println("Public Fields");
for(Field field : fields){
System.out.println(field);
}
System.out.println("\nPublic Ctors");
for(Constructor ctor : ctors){
System.out.println(ctor);
}
System.out.println("\nPublic Methods");
for(Method method : methods){
System.out.println(method);
}
}
}
В данном случае используем методы getFields(), getMethods() и getConstructors() для получения всех публичных компонентов класса, в том числе унаследованных от класса Person.
Консольный вывод программы:
Public Fields Public Ctors public Person(java.lang.String,int) public Person() Public Methods public java.lang.String Person.getName() public int Person.getAge() public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() public final void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException
Преимуществом классов Field/Method/Constructor является то, что в Java уже по умолчанию реализован полноценный вывод компонента, что очень удобно при выводе на консоль. И в данном случае мы видим, что методы вывели только
публичные компоненты, в том числе и унаследованные от класса Object.
Теперь применим другой набор методов для получения информации о классе - getDeclaredFields()/getDeclaredMethods()/getDeclaredConstructors():
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// получаем класс для класса Person
Class personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
Method[] methods = personClass.getDeclaredMethods();
Constructor[] ctors = personClass.getDeclaredConstructors();
System.out.println("Fields");
for(Field field : fields){
System.out.println(field);
}
System.out.println("\nCtors");
for(Constructor ctor : ctors){
System.out.println(ctor);
}
System.out.println("\nMethods");
for(Method method : methods){
System.out.println(method);
}
}
}
Консольный вывод:
Fields private java.lang.String Person.name private int Person.age Ctors public Person(java.lang.String,int) public Person() Methods public java.lang.String Person.getName() public int Person.getAge()
Подобным образом мы можем исследовать и встроенные типы данных. Например, исследуем тип String (ограничимся конструкторами):
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
Class strClass = String.class;
Constructor[] ctors = strClass.getConstructors();
for(Constructor ctor : ctors){
System.out.println(ctor);
}
}
}
Консольный вывод:
public java.lang.String(java.lang.StringBuilder) public java.lang.String(byte[],int,int,java.nio.charset.Charset) public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],java.nio.charset.Charset) public java.lang.String(byte[],int,int) public java.lang.String(byte[]) public java.lang.String(java.lang.StringBuffer) public java.lang.String(char[],int,int) public java.lang.String(char[]) public java.lang.String(java.lang.String) public java.lang.String() public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],int) public java.lang.String(byte[],int,int,int) public java.lang.String(int[],int,int)
Ряд методов класса Class позволяют проверить исследуемый тип:
boolean isAnonymousClass(): возвращает true, если тип является анонимным классом
boolean isArray(): возвращает true, если тип представляет массив
boolean isPrimitive(): возвращает true, если тип является примитивным типом
boolean isInterface(): возвращает true, если объект представляет интерфейс
boolean isEnum(): возвращает true, если объект представляет перечисление
boolean isRecord(): возвращает true, если объект представляет класс record
boolean isInstance(Object obj): возвращает true, если объект-параметр является экземпляром этого типа
boolean isSealed(): возвращает true, если класс определен с модификатором sealed
Class getSuperclass(): возвращает суперкласс для текущего типа
Пример применения некоторых из этих методов:
import java.lang.reflect.*;
public class Program{
public static void main(String[] args) {
// получаем класс для типа Person
Class cl = Person.class;
// получаем имя класса
System.out.println("Name: " + cl.getName());
// получаем суперкласс
System.out.println("superclass: " + cl.getSuperclass());
// проверяем, является ли тип примитивным типом
System.out.println("primitive: " + cl.isPrimitive());
// проверяем, является ли тип массивом
System.out.println("array: " + cl.isArray());
// проверяем, является ли тип классом record
System.out.println("record: " + cl.isRecord());
// проверяем, является ли тип интерфейсом
System.out.println("interface: " + cl.isInterface());
// проверям, что tom - объект класса
Person tom = new Person("Tom", 41);
System.out.println("tom is a Person:: " + cl.isInstance(tom));
// проверям, что message - объект класса
String message = "hello";
System.out.println("message is a Person:: " + cl.isInstance(message));
}
}
record Person(String name, int age){}
Консольный вывод программы:
Name: Person superclass: class java.lang.Record primitive: false array: false record: true interface: false tom is a Person:: true message is a Person:: false