# Java - Reflection
###### tags: `Java` `Advanced Java`
## 什麼是Reflection(反射):
在運行Java時能夠直接得到某個class的全部內容,就稱為反射
也就是我們可以繞過編譯階段,直接拿到我們想要使用的東西(不論是constructors, fields或是methods)
### 第一步:取得該物件的類別
```
// method1
Class c1 = Student.class;
System.out.println(c);
// method2
Student s = new Student();
Class c2 = s.getClass();
System.out.println(c1);
// method3
Class c3 = Class.forName("類別名稱(路徑)")
```
### 第二步:取得Constructor
1. getDeclaredConstructor()
2. getDeclaredConstructor(class type of parameters)
```
// 1. get a class object
Class c = Student.class;
// 2. get constructor
// getDeclaredConstructors可以不論private或public全部都取得
Constructor[] constructors = c.getDeclaredConstructors();
// 3. iterate constructor to take a look
for (Constructor constructor: constructors) {
System.out.println(constructor.getName() + "->" + constructor.getParameterCount());
}
```
第二步驟取得constructor的地方,可以查看API檔案去利用不同的方法應對不同寫法的constructor
將constructor轉換成類別
```
Constructor cons = c.getDeclaredConstructor();
Student s = (Student) cons.newInstance();
```
也就是說我又可以拿這個類別去建立物件(這就是為什麼這個作法被稱作反射)
```
// 取得有參數的constructor(參數要+.class)
Constructor cons2 = c.getDeclaredConstructor(String.class, int.class);
// 將constructor變成物件
Student s2 = (Student) cons2.newInstance("John", 20);
System.out.println(s2);
```
補充:將private的構造強制打開(暴力反射):setAccessible(true)
### 第三步:取得類別的變數
1. getDeclaredFields()
2. getDeclaredFileds(String name)
```
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + "---" + field.getType());
}
Field f = c.getDeclaredField("age");
System.out.println(f.getName() + "---" + f.getType());
```
幫變數賦值
```
f.setAccessible(true); // 暴力反射
Student s = new Student();
f.set(s, 99);
```
取值
```
int age = (int) f.get(s);
System.out.println(age); // 99
```
### 第四步:取得類別的方法
其實API跟上面三個步驟都很類似
1. getDeclaredMethods() 取得全部的方法
2. getDeclaredMethod() 取得單一方法
取得全部的方法
```
Class dog = Dog.class;
Method[] methods = dog.getDeclaredMethods();
for (Method method: methods) {
System.out.println(method.getName() + "---" + method.getReturnType() + "---" + method.getParameterCount());
}
```
取得單一方法
```
// get certain method
Method m = dog.getDeclaredMethod("eat");
Method m2 = dog.getDeclaredMethod("eat", String.class);
```
觸發執行方法
* 無參數,傳入實例化物件即可
* 有參數,傳入物件與該類型參數
```
// invoke method
Dog d = new Dog();
m.invoke(d);
m2.setAccessible(true);
String result = (String) m2.invoke(d, "DOGGGG");
System.out.println(result);
```
## 反射的小應用:突破泛型
原理:泛型只有在編譯階段的時候才起作用,編譯之後其實就只是個list class
這時候可以利用反射把list的方法拿出來利用
在一個Integer list裡面加入String類型變數
```
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(97);
list.add(98);
list.add(99);
Class c = list.getClass();
Method add = c.getDeclaredMethod("add", Object.class);
add.invoke(list, "A String!!!!");
System.out.println(list);
}
```
> [97, 98, 99, A String!!!!]
## 反射的主要應用:做通用框架
假設一個場景:
> 我需要設計一個通用功能,可以接收任何物件,然後在完全不清楚物件內容的情況下把該物件的變數名稱跟值都存到文件中
該怎麼下手呢?
分析:
1. 定義一個方法,可以接收任何物件
2. 收到物件後可以分析該物件的內容
3. 利用反射獲取物件.class,然後進而獲取該類別之成員變數
4. 遍歷變數後存入文件
```
public class CommonUtility {
public static void save(Object object) {
try {
PrintStream ps = new PrintStream(new FileOutputStream("filepath", true));
// 1. create a class object
Class c = object.getClass();
ps.println("======" + c.getSimpleName() + "======");
// 2. get all the field members of that object
Field[] fields = c.getDeclaredFields();
for (Field field: fields) {
// 3. store variable names
String name = field.getName();
field.setAccessible(true);
// 4. store variable values
String value = field.get(object) + "";
// print out
ps.println(name + "=" + value);
}
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
然後在主方法傳入物件實例,就可以看到設定後的結果了
```
public static void main(String[] args) throws Exception {
Student s = new Student();
s.setName("John Wick");
s.setAge(50);
s.setSex('M');
s.setClassName("Class S");
s.setHobby("Being a Agent");
CommonUtility.save(s);
Teacher t = new Teacher();
t.setName("Tony Stark");
t.setSex('M');
t.setSalary(500000);
CommonUtility.save(t);
}
```
Output Result:
```
======Student======
name=John Wick
sex=M
age=50
className=Class S
hobby=Being a Agent
======Teacher======
name=Tony Stark
sex=M
salary=500000.0
```