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; } 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下。