java.lang.reflect.Field类
Field类提供有关类或接口的单个字段的信息和动态访问。Field还允许在获取或设置访问操作期间扩大类型转换,但如果发生缩小,则会抛出IllegalArgumentException 。
要获得Field对象,我们首先要编写一个类:
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 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);
}
}
这就是我们获取类字段列表的方式,稍后我们将使用它。结果如下:
现在让我们弄清楚我们可以用这些数据做什么。下面说说Field类的方法:
方法 | 描述 |
---|---|
获取类型() | 返回一个 Class 对象,该对象标识由此表示的字段的声明类型场地目的。 |
getAnnotatedType() | 返回一个注释类型表示使用类型来指定此 Field 表示的字段的声明类型的对象。 |
getGenericType() | 返回一个类型表示此表示的字段的声明类型的对象场地目的。 |
获取名称() | 返回此表示的字段的名称场地目的。 |
getModifiers() | 返回一个 int 编码的字段的 Java 语言修饰符场地目的。 |
获取注解() | 返回此字段的注释。如果没有注释,它返回一个空数组。 |
getType()、getName() 和 getModifiers() 方法
我们可以使用getType()方法来获取字段的类型。让我们写一个方法:
static void printTypes(List<Field> fields){
fields.forEach(e -> System.out.println(e.getType()));
}
我们得到这个结果:
int
boolean
类 java.lang.String
int
int
现在让我们在我们的类中添加一个方法来获取字段的名称。这将使我们更容易浏览我们班级的领域。
static void printTypesAndNames(List<Field> fields){
fields.forEach(e -> System.out.printf("Field type - %s\nField name - %s\n\n", e.getType(), e.getName()));
}
现在结果更容易理解了:
类 字段名 - name
字段类型 - int
字段名 - age
字段类型 - boolean
字段名 - isMale
字段类型 - java.lang.String
类 字段名 - address
字段类型 - int
字段名 - MAX_AGE
字段类型 - int
字段名称 - MIN_AGE
伟大的!让我们再修改一下我们的方法!我们将在此处添加访问修饰符
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())));
}
让我们看看e.getModifiers()返回什么。此方法返回一个int,让我们确定字段访问修饰符的状态。Modifier类包含负责字段的每个特定修饰符的静态变量。
让我们将返回值包装在Modifier.toString()中并立即将其值作为文本获取:
字段名 - name
修饰符 - private
字段类型 - int
字段名 - age
修饰符 - private
字段类型 - boolean
字段名 - isMale
修饰符 - public
字段类型 - class java.lang.String
字段名 - address
修饰符 - protected
字段类型 - int
字段名 - MAX_AGE
修饰符 - public static final
字段类型 - int
字段名 - MIN_AGE
修饰符 - public static final
这是没有Modifier.toString()的样子:
字段名 - name
修饰符 - 2
字段类型 - int
字段名 - age
修饰符 - 2
字段类型 - boolean
字段名 - isMale
修饰符 - 1
字段类型 - class java.lang.String
字段名 -地址
修饰符 - 4
字段类型 - int
字段名称 - MAX_AGE
修饰符 - 25
字段类型 - int
字段名称 - MIN_AGE
修饰符 - 25
getAnnotations()、getAnnotatedType() 和 getGenericType() 方法
让我们修改Person类以使用这些方法。我们将编写自己的注释并将其应用于我们的字段。我们将添加更多字段。
让我们创建两个注释。我们将 Pig Latin 中的变量名传递给一个,我们将对元素使用第二个:
@Target(value=ElementType.FIELD)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Name {
String name();
}
@Target({ ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Number {
}
我们将更改主类和Person类:
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())));
}
是时候看看我们的方法的结果并弄清楚它们的用途了:
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返回给定字段的注释(如果有)。我们得到了该字段的注释,我们可以完美地看到它。
-
getGenericType可以让您正确显示参数化类型。
-
getAnnotations返回应用于我们对象的注释。
这就是我们如何轻松获取类中每个字段的所有数据,以及它的访问修饰符、注释和数据类型的方法。
java.lang.reflect.Method类
极好的!我们已经谈到了我们班的领域。现在是时候谈谈方法了。
要获得Method对象,我们调用getMethod方法,将我们的方法名称传递给它。这是获取Method对象的基本方法:
Method getNameMethod = Person.class.getMethod("getName");
我们将继续与我们的班级合作。让我们添加 getter 和 setter,以及 hashCode、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{" +
"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);
}
}
现在让我们准备一组方法,我们将使用Method类检查这些方法。以下是最重要方法的列表:
方法 | 描述 |
---|---|
获取名称() | 返回方法的名称。 |
getModifiers() | 返回此方法的访问修饰符。 |
获取返回类型() | 返回方法的返回类型。 |
getGenericReturnType() | 返回方法的返回类型,考虑泛型方法。 |
获取参数类型() | 返回一个方法参数数组。 |
getGenericParameterTypes() | 返回一个方法参数数组,考虑泛型方法。 |
getExceptionTypes() 方法 | 返回方法可以抛出的异常。 |
getGenericExceptionTypes() 方法 | 返回方法可以抛出的异常,考虑参数化类型。 |
获取注解() | 返回方法的注释,包括父注释。 |
getDeclaredAnnotations() | 返回方法的注释,不包括父注释。 |
要获取类的方法数组,我们可以调用此方法:
Method[] methods = Person.class.getDeclaredMethods();
它将为我们提供类中的所有方法。
getName() 和 getModifiers() 方法
我们可以使用getName获取每个方法的名称:
static List<String> getMethodsName(Method[] methods) {
return Arrays.asList(methods)
.stream()
.map(Method::getName)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}
现在要获取修饰符,让我们编写一个使用getModifiers 的方法:
static List<String> getModifiers(Method[] methods) {
return Arrays
.stream(methods)
.map(Method::getModifiers)
.map(String::valueOf)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}
这是我们的主要方法:
public static void main(String[] args) {
Method[] methods = Person.class.getDeclaredMethods();
System.out.println(getMethodsName(methods));
System.out.println(getModifiers(methods));
}
我们的结果:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
我们所有的方法都有public修饰符,所以最后一个方法返回一个数组。如果我们修改我们的代码,我们将看到我们的修饰符本身:
public static void main(String[] args) {
Method[] methods = Person.class.getDeclaredMethods();
System.out.println(getMethodsName(methods));
System.out.println(modifyModifiers(getModifiers(methods)));
}
、公共]
getReturnedType()
这个方法让我们得到方法的返回类型:
static void getReturnedType(Method[] methods) {
Arrays.stream(methods)
.map(Method::getReturnType)
.forEach(System.out::println);
}
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void
getGenericReturnType()
让我们为Person类提供一个方法,该方法返回包装在参数化类型中的类型,并尝试获取其返回值:
public List<String> someMethod() {
// Very useful and important method
return null;
}
我们将更新我们的主要方法:
static void getGenericReturnType(Method[] methods) {
Arrays.stream(methods)
.map(Method::getGenericReturnType)
.forEach(System.out::println);
}
我们方法的结果:
boolean
class java.lang.String
int
void
class java.lang.String
boolean
int
void
void
void
java.util.List<java.lang.String>
getParameterTypes() 和 getGenericParameterTypes() 方法
我们继续修改我们的Person类,添加两个方法:
public List<String> someMethod(List<String> list, String s) {
// Very useful and important method
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 {
Method currentMethod = Person.class.getMethod("someMethod", List.class, String.class);
getParameterTypes(currentMethod);
System.out.println();
getGenericParameterTypes(currentMethod);
}
运行我们的代码,我们将看到这些方法有何不同以及它们返回什么:
类 java.lang.String
java.util.List<java.lang.String>
类 java.lang.String
getExceptionTypes() 和 getGenericExceptionTypes() 方法
我们可以使用这些方法来获取我们的方法可以抛出的异常数组,以及参数化类型的异常(如果有的话)。让我们使用一个具有隐藏静态类的新示例:
private static class Processor {
private void init() {}
private void process() throws IOException {}
}
我们将在处理器类上调用方法:
public static void main(String... args) throws NoSuchMethodException {
Method method = Processor.class.getDeclaredMethod("process");
Type[] type = method.getExceptionTypes();
System.out.println(Arrays.toString(type));
}
现在我们可以看到我们的异常:
现在让我们参数化类型。我们将修改我们的主类:
private static class Processor<E extends IOException> {
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对象,它是类型变量的通用父接口。在其中,我们现在可以获得内部参数,即我们的嵌套异常:
类 java.io.IOException
getAnnotations() 和 getDeclaredAnnotations() 方法
让我们继续使用这个新类并为其添加一些注释。我们将创建自己的Annotation注释:
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation {
public String key();
public String value();
}
并将其应用于我们的方法:
@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();
}
}
我们主要方法的实现:
public static void main(String... args) {
Class clazz = Processor.class;
getMethodAnnotations(clazz);
}
结果屏幕输出是:
[@com.company.Main&Annotation(key="key", value="value")]
这就是我们如何获取已应用于我们的方法的注释,并且getAnnotations方法还允许我们访问该类的父注释。
今天我们了解了反射如何与方法和字段一起工作,以及我们可以通过它获取哪些数据!
GO TO FULL VERSION