Java 反射练习

OOP h18

构造一个「类厂」:MyClassFactory

该类能够用一个 .ini 文件初始化,.ini 文件的格式参考 my_config.ini,测试的时候这个文件名字可能改变,文件中包括中文,且文件一定为 UTF-8 编码

.ini 文件中描述了这个类厂能够动态创建的和类的初始属性

动态构造各种类,并且用.ini中的属性初始化,测试的时候 类名可能变化,例如 Orange.class ….

测试的时候,可能给其他对象,测试时候给的对象一定满足如下条件:

  • 有空的构造函数
  • 其中只包括了 Integer,String 两种属性,并且有 setXxx getXxx 方法

将要动态创建的“类”抽象成 Param 类。

0x00 Param

该类应该具有成员变量,存储“类”和属性的对应关系(Map),而属性和属性值也具有对应关系(Map)。

Param 包含成员变量 allParamsMap,类型为 Map<String, Map<String, Object>>

构造方法可以直接调用 readLines 读取文件转换格式。

新建 getParamMap 方法,返回对应“类”名的属性映射 Map<String, Object>

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.huawei.classroom.student.h18;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
* @author super
*/
public class Param {
/**
* HashMap<类名, HashMap<属性名, 属性值>>
*/
private final Map<String, Map<String, Object>> allParamsMap = new HashMap<>();

public Param(String filename) throws IOException {
readLines(filename);
}

public Map<String, Object> getParamMap(String className) {
return allParamsMap.get(className);
}

private void readLines(String filename) throws IOException {
InputStream in = new FileInputStream(filename);// 读
InputStreamReader inReader = new InputStreamReader(in, StandardCharsets.UTF_8);// 读
LineNumberReader lineReader = new LineNumberReader(inReader);// 读
String line = lineReader.readLine();// 读一行

while (line != null) {// 没到文件末尾
if (line.startsWith("#") || line.trim().length() == 0) {
// 这里不能 continue 否则会死循环
} else {
// 一行的格式:[类的完整路径名].[属性]="一个字符串"/或者一个整数
String[] strs = line.split("=");
String left = strs[0];// [类的完整路径名].[属性]
String right = strs[1];// "一个字符串"/或者一个整数
// [类的完整路径名].[属性]
int dotPos = left.lastIndexOf(".");
String className = left.substring(0, dotPos);// [类的完整路径名]
String paramName = left.substring(dotPos + 1);// [属性]
// [属性]="一个字符串"/或者一个整数
Map<String, Object> paramMap = null;
if (allParamsMap.containsKey(className)) {
paramMap = allParamsMap.get(className);// 已经有这个类名了,把键对应的值赋给它
} else {
paramMap = new HashMap<>();// 没有这个类,新建
}
// "一个字符串"/或者一个整数
if (right.startsWith("\"")) {
paramMap.put(paramName, right.substring(1, right.length() - 1));// 是个字符串
} else {
paramMap.put(paramName, Integer.parseInt(right));// 是个整数,可以用 valueOf
}
allParamsMap.put(className, paramMap);// put 进去
}
line = lineReader.readLine();// 再读一行
}
}
}

0x01 MyClassFactory

动态调用赋值,需要给“类”新建 setter 方法(getter 是 Test 用的)。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.huawei.classroom.student.h18;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
* @author super
*/
public class MyClassFactory {
private final Param params;// 成员变量就是 Param

public MyClassFactory(String filename) throws IOException {
params = new Param(filename);// 新建 Param,可以存所有类以及对应的属性/属性值
}

/**
*
* @param clazz 类
* @param <T> 类型
* @return 类型
* setter (getter in Test)
*/
public <T> T createInstance(Class<T> clazz)
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
String className = clazz.getName();// 类名
T t = clazz.newInstance();
Map<String, Object> paramMap = params.getParamMap(className);// 取类的属性 Map
for (String param : paramMap.keySet()) {// 遍历所有属性,依次创建 setter
Object value = paramMap.get(param);// 属性值
String methodName = "set" + param.substring(0, 1).toUpperCase() + param.substring(1);// setter 名
dynamicInvokeMethod(t, methodName, value);// 动态调用类 t 的 setXxx 方法,把属性 xxx 赋值为 value
}
return t;
}

/**
* 动态调用赋值
*/
public void dynamicInvokeMethod(Object obj, String methodName, Object... values)// 多参数
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
int i;
Class<?>[] classes = new Class[values.length];// 每一个参数的类型
for (i = 0; i < values.length; i++) {
classes[i] = values[i].getClass();
}
Method method = obj.getClass().getMethod(methodName, classes);// 创建 Method
method.invoke(obj, values);// 调用 Method 赋值
}
}