21.2 应用示例

21.2 应用示例

介绍了Class的这么多方法,有什么用呢?我们看个简单的示例,利用反射实现一个简单的通用序列化/反序列化类SimpleMapper ,它提供两个静态方法:

1
2
public static String toString(Object obj)
public static Object fromString(String str)

toString将对象obj转换为字符串,fromString将字符串转换为对象。为简单起见,我们只支持最简单的类,即有默认构造方法,成员类型只有基本类型、包装类或String。另外,序列化的格式也很简单,第一行为类的名称,后面每行表示一个字段,用字符’=’分隔,表示字段名称和字符串形式的值。我们先看SimpleMapper的用法,如代码清单21-1所示。

代码清单21-1 简单的通用序列化/反序列化类SimpleMapper的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public static String toString(Object obj)
public static Object fromString(String str)
public class SimpleMapperDemo {
static class Student {
String name;
int age;
Double score;
//省略了构造方法, getter/setter和toString方法
}
public static void main(String[] args) {
Student zhangsan = new Student("张三", 18, 89d);
String str = SimpleMapper.toString(zhangsan);
Student zhangsan2 = (Student) SimpleMapper.fromString(str);
System.out.println(zhangsan2);
}
}

代码先调用toString方法将对象转换为了String,然后调用fromString方法将字符串转换为了Student,新对象的值与原对象是一样的,输出如下所示:

1
Student [name=张三, age=18, score=89.0]

我们来看SimpleMapper的示例实现(主要用于演示原理), toString的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static String toString(Object obj) {
try {
Class<? > cls = obj.getClass();
StringBuilder sb = new StringBuilder();
sb.append(cls.getName() + "\n");
for(Field f : cls.getDeclaredFields()) {
if(! f.isAccessible()) {
f.setAccessible(true);
}
sb.append(f.getName() + "=" + f.get(obj).toString() + "\n");
}
return sb.toString();
} catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
}

代码比较简单,我们就不赘述了。fromString的代码为:

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
public static Object fromString(String str) {
try {
String[] lines = str.split("\n");
if(lines.length < 1) {
throw new IllegalArgumentException(str);
}
Class<? > cls = Class.forName(lines[0]);
Object obj = cls.newInstance();
if(lines.length > 1) {
for(int i = 1; i < lines.length; i++) {
String[] fv = lines[i].split("=");
if(fv.length ! = 2) {
throw new IllegalArgumentException(lines[i]);
}
Field f = cls.getDeclaredField(fv[0]);
if(! f.isAccessible()){
f.setAccessible(true);
}
setFieldValue(f, obj, fv[1]);
}
}
return obj;
} catch(Exception e) {
throw new RuntimeException(e);
}
}

它调用了setFieldValue方法对字段设置值,其代码为:

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
private static void setFieldValue(Field f, Object obj, String value)
throws Exception {
Class<? > type = f.getType();
if(type == int.class) {
f.setInt(obj, Integer.parseInt(value));
} else if(type == byte.class) {
f.setByte(obj, Byte.parseByte(value));
} else if(type == short.class) {
f.setShort(obj, Short.parseShort(value));
} else if(type == long.class) {
f.setLong(obj, Long.parseLong(value));
} else if(type == float.class) {
f.setFloat(obj, Float.parseFloat(value));
} else if(type == double.class) {
f.setDouble(obj, Double.parseDouble(value));
} else if(type == char.class) {
f.setChar(obj, value.charAt(0));
} else if(type == boolean.class) {
f.setBoolean(obj, Boolean.parseBoolean(value));
} else if(type == String.class) {
f.set(obj, value);
} else {
Constructor<? > ctor = type.getConstructor(
new Class[] { String.class });
f.set(obj, ctor.newInstance(value));
}
}

setFieldValue根据字段的类型,将字符串形式的值转换为了对应类型的值,对于基本类型和String以外的类型,它假定该类型有一个以String类型为参数的构造方法。

示例的完整代码在github上,地址为 https://github.com/swiftma/program-logic ,位于包shuo.laoma.dynamic.c84下。