1. Overview
1.概述
Reflection is the ability for computer software to inspect its structure at runtime. In Java, we achieve this by using the Java Reflection API. It allows us to inspect the elements of a class such as fields, methods or even inner classes, all at runtime.
反思是计算机软件在运行时检查其结构的能力。在Java中,我们通过使用Java Reflection API来实现这一目标。它允许我们在运行时检查类的元素,如字段、方法甚至是内部类。
This tutorial will focus on how to retrieve the fields of a Java class, including private and inherited fields.
本教程将重点介绍如何检索Java类的字段,包括私有和继承的字段。
2. Retrieving Fields from a Class
2.从一个类中检索字段
Let’s first have a look at how to retrieve the fields of a class, regardless of their visibility. Later on, we’ll see how to get inherited fields as well.
让我们先看看如何检索一个类的字段,不管它们是否可见。稍后,我们将看到如何获得继承的字段。
Let’s start with an example of a Person class with two String fields: lastName and firstName. The former is protected (that’ll be useful later) while the latter is private:
让我们从一个Person类的例子开始,该类有两个String字段。lastName和firstName。前者是保护的(这在后面会很有用),而后者是私有的:。
public class Person {
protected String lastName;
private String firstName;
}
We want to get both lastName and firstName fields using reflection. We’ll achieve this by using the Class::getDeclaredFields method. As its name suggests, this returns all the declared fields of a class, in the form of a Field array:
我们想通过反射来获得lastName和firstName两个字段。我们将通过使用Class::getDeclaredFields方法来实现这一目标。顾名思义,它以Field数组的形式返回一个类的所有declared字段:
public class PersonAndEmployeeReflectionUnitTest {
/* ... constants ... */
@Test
public void givenPersonClass_whenGetDeclaredFields_thenTwoFields() {
Field[] allFields = Person.class.getDeclaredFields();
assertEquals(2, allFields.length);
assertTrue(Arrays.stream(allFields).anyMatch(field ->
field.getName().equals(LAST_NAME_FIELD)
&& field.getType().equals(String.class))
);
assertTrue(Arrays.stream(allFields).anyMatch(field ->
field.getName().equals(FIRST_NAME_FIELD)
&& field.getType().equals(String.class))
);
}
}
As we can see, we get the two fields of the Person class. We check their names and types which matches the fields definitions in the Person class.
我们可以看到,我们得到了Person类的两个字段。我们检查它们的名称和类型,它们与Person类中的字段定义相匹配。
3. Retrieving Inherited Fields
3.检索继承的字段
Let’s now see how to get the inherited fields of a Java class.
现在让我们看看如何获得一个Java类的继承字段。
To illustrate this, let’s create a second class named Employee extending Person, with a field of its own:
为了说明这一点,让我们创建第二个名为Employee的类来扩展Person,并有一个自己的字段。
public class Employee extends Person {
public int employeeId;
}
3.1. Retrieving Inherited Fields on a Simple Class Hierarchy
3.1.检索简单类层次结构上的继承字段
Using Employee.class.getDeclaredFields() would only return the employeeId field, as this method doesn’t return the fields declared in superclasses. To also get inherited fields we must also get the fields of the Person superclass.
使用Employee.class.getDeclaredFields()将只返回employeeId字段,因为这个方法并不返回超类中声明的字段。为了获得继承的字段,我们也必须获得Person超类的字段。
Of course, we could use the getDeclaredFields() method on both Person and Employee classes and merge their results into a single array. But what if we don’t want to explicitly specify the superclass?
当然,我们可以在Person 和Employee 类上使用getDeclaredFields()方法,并将其结果合并为一个数组。但是如果我们不想明确地指定超类呢?
In this case, we can make use of another method of the Java Reflection API: Class::getSuperclass. This gives us the superclass of another class, without us needing to know what that superclass is.
在这种情况下,我们可以利用Java Reflection API的另一个方法。Class::getSuperclass。这就给了我们另一个类的超类,而我们不需要知道这个超类是什么。
Let’s gather the results of getDeclaredFields() on Employee.class and Employee.class.getSuperclass() and merge them into a single array:
让我们收集getDeclaredFields()对Employee.class和Employee.class.getSuperclass()的结果,将它们合并成一个数组。
@Test
public void givenEmployeeClass_whenGetDeclaredFieldsOnBothClasses_thenThreeFields() {
Field[] personFields = Employee.class.getSuperclass().getDeclaredFields();
Field[] employeeFields = Employee.class.getDeclaredFields();
Field[] allFields = new Field[employeeFields.length + personFields.length];
Arrays.setAll(allFields, i ->
(i < personFields.length ? personFields[i] : employeeFields[i - personFields.length]));
assertEquals(3, allFields.length);
Field lastNameField = allFields[0];
assertEquals(LAST_NAME_FIELD, lastNameField.getName());
assertEquals(String.class, lastNameField.getType());
Field firstNameField = allFields[1];
assertEquals(FIRST_NAME_FIELD, firstNameField.getName());
assertEquals(String.class, firstNameField.getType());
Field employeeIdField = allFields[2];
assertEquals(EMPLOYEE_ID_FIELD, employeeIdField.getName());
assertEquals(int.class, employeeIdField.getType());
}
We can see here that we’ve gathered the two fields of Person as well as the single field of Employee.
我们在这里可以看到,我们已经收集了Person的两个字段,以及Employee的单一字段。
But, is the private field of Person really an inherited field? Not so much. That would be the same for a package-private field. Only public and protected fields are considered inherited.
但是,Person的private字段真的是一个继承的字段吗?不是这样的。这对于package-private字段来说是一样的。只有public和protected字段被认为是继承的。
3.2. Filtering public and protected Fields
3.2.过滤public和protected字段
Unfortunately, no method in the Java API allows us to gather public and protected fields from a class and its superclasses. The Class::getFields method approaches our goal as it returns all public fields of a class and its superclasses, but not the protected ones.
不幸的是,Java API中没有任何方法允许我们从一个类及其超类中收集公共和受保护字段。Class::getFields方法接近我们的目标,因为它返回一个类及其超类的所有public字段,但不返回protected字段。
The only way we have to get only inherited fields is to use the getDeclaredFields() method, as we just did, and filter its results using the Field::getModifiers method. This one returns an int representing the modifiers of the current field. Each possible modifier is assigned a power of two between 2^0 and 2^7.
我们要想只获得继承的字段,唯一的方法是使用getDeclaredFields()方法,就像我们刚才做的那样,并使用Field::getModifiers方法过滤其结果。这个方法返回一个int,代表当前字段的修改器。每个可能的修饰符都被分配了一个介于2^0和2^7之间的2次方。。
For example, public is 2^0 and static is 2^3. Therefore calling the getModifiers() method on a public and static field would return 9.
例如,public是2^0,static是2^3。因此,在一个public和static字段上调用getModifiers()方法将返回9。
Then, it’s possible to perform a bitwise and between this value and the value of a specific modifier to see if that field has that modifier. If the operation returns something else than 0 then the modifier is applied, otherwise not.
然后,可以在这个值和特定的修改器的值之间进行bitwise and,以查看该字段是否有该修改器。如果该操作的返回值不是0,那么该修改器就被应用,否则就没有。
We’re lucky as Java provides us with a utility class to check if modifiers are present in the value returned by getModifiers(). Let’s use the isPublic() and isProtected() methods to gather only inherited fields in our example:
我们很幸运,因为Java为我们提供了一个实用类,以检查getModifiers()返回的值中是否存在修改器。让我们使用isPublic()和isProtected()方法,在我们的例子中只收集继承的字段:。
List<Field> personFields = Arrays.stream(Employee.class.getSuperclass().getDeclaredFields())
.filter(f -> Modifier.isPublic(f.getModifiers()) || Modifier.isProtected(f.getModifiers()))
.collect(Collectors.toList());
assertEquals(1, personFields.size());
assertTrue(personFields.stream().anyMatch(field ->
field.getName().equals(LAST_NAME_FIELD)
&& field.getType().equals(String.class))
);
As we can see, the result doesn’t carry the private field anymore.
我们可以看到,结果中不再有private字段。
3.3. Retrieving Inherited Fields on a Deep Class Hierarchy
3.3.检索深层类层次上的继承字段
In the above example, we worked on a single class hierarchy. What do we do now if we have a deeper class hierarchy and want to gather all the inherited fields?
在上面的例子中,我们在一个单一的类的层次结构上工作。如果我们有一个更深的类层次结构,并且想收集所有的继承字段,我们现在该怎么做?
Let’s assume we have a subclass of Employee or a superclass of Person – then obtaining the fields of the whole hierarchy will require to check all the superclasses.
假设我们有一个Employee的子类或者一个Person –的超类,那么获得整个层次结构的字段就需要检查所有的超类。
We can achieve that by creating a utility method that runs through the hierarchy, building the complete result for us:
我们可以通过创建一个贯穿层次结构的实用方法来实现,为我们构建完整的结果。
List<Field> getAllFields(Class clazz) {
if (clazz == null) {
return Collections.emptyList();
}
List<Field> result = new ArrayList<>(getAllFields(clazz.getSuperclass()));
List<Field> filteredFields = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> Modifier.isPublic(f.getModifiers()) || Modifier.isProtected(f.getModifiers()))
.collect(Collectors.toList());
result.addAll(filteredFields);
return result;
}
This recursive method will search public and protected fields through the class hierarchy and returns all that have been found in a List.
这个递归方法将通过类的层次结构搜索公共和保护字段,并在List中返回所有被发现的字段。
Let’s illustrate it with a little test on a new MonthEmployee class, extending the Employee one:
让我们用一个新的MonthEmployee类的小测试来说明,它扩展了Employee类。
public class MonthEmployee extends Employee {
protected double reward;
}
This class defines a new field – reward. Given all the hierarchy class, our method should give us the following fields definitions: Person::lastName, Employee::employeeId and MonthEmployee::reward.
这个类定义了一个新的字段–reward。考虑到所有的层次结构类,我们的方法应该给我们提供以下字段的定义。Person::lastName, Employee::employeeId 和 MonthEmployee::reward。
Let’s call the getAllFields() method on MonthEmployee:
让我们在MonthEmployee上调用getAllFields()方法。
@Test
public void givenMonthEmployeeClass_whenGetAllFields_thenThreeFields() {
List<Field> allFields = getAllFields(MonthEmployee.class);
assertEquals(3, allFields.size());
assertTrue(allFields.stream().anyMatch(field ->
field.getName().equals(LAST_NAME_FIELD)
&& field.getType().equals(String.class))
);
assertTrue(allFields.stream().anyMatch(field ->
field.getName().equals(EMPLOYEE_ID_FIELD)
&& field.getType().equals(int.class))
);
assertTrue(allFields.stream().anyMatch(field ->
field.getName().equals(MONTH_EMPLOYEE_REWARD_FIELD)
&& field.getType().equals(double.class))
);
}
As expected, we gather all the public and protected fields.
正如预期的那样,我们收集了所有的public和protected字段。
4. Conclusion
4.总结
In this article, we saw how to retrieve the fields of a Java class using the Java Reflection API.
在这篇文章中,我们看到了如何使用Java Reflection API来检索一个Java类的字段。
We first learned how to retrieve the declared fields of a class. After that, we saw how to retrieve its superclass fields as well. Then, we learned to filter out non-public and non-protected fields.
我们首先学习了如何检索一个类的声明字段。之后,我们看到如何检索其超类的字段。然后,我们学习了如何过滤掉非公共的和非保护的字段。
Finally, we saw how to apply all of this to gather the inherited fields of a multiple class hierarchy.
最后,我们看到如何应用所有这些来收集一个多类层次结构的继承字段。
As usual, the full code for this article is available over on our GitHub.
像往常一样,本文的完整代码可在我们的GitHub上获得,。