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
字段類型 - class 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
Modifiers - 2
字段類型 - int
字段名 - age
Modifiers - 2
字段類型 - boolean
字段名 - isMale
Modifiers - 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