封面《喫茶ステラと死神の蝶》
问题描述
现在我有一下几个类,需要在 UpdateForm
和 StringModel
之间转换。同时对 StringModel
的部分字段进行一些非空检查。实际的 StringModel
和 UpdateForm
字段较多
1 2 3 4 5 @Data public class BaseModel { private Integer id; }
1 2 3 4 5 6 7 8 9 10 11 12 13 @Data @EqualsAndHashCode(callSuper = true) public class StringModel extends BaseModel { private String name; private String choice; @Override public String toString () { return "model {id=" + this .getId() +",name=" +this .name+",choice=" +this .choice + "}" ; } }
1 2 3 4 5 6 7 8 9 @Data @NoArgsConstructor @AllArgsConstructor public class MultiChoiceForm { private String label; private Boolean selected=false ; }
1 2 3 4 5 6 7 8 9 10 @Data public class UpdateForm { private Integer id; private String name; private List<MultiChoiceForm> choice; }
通过反射获取属性
在进行一番思考后我决定使用反射来解决这一问题,直接使用 getDeclaredFields()
函数获取类的所有私有成员
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 public class App { public static void main ( String[] args ) { test1(); } public static void test1 () { StringModel model = new StringModel (); model.setId(1 ); model.setName("qxdn" ); model.setChoice("123" ); System.out.printf("model: %s\n" ,model); Field[] fields = model.getClass().getDeclaredFields(); for (Field field:fields){ try { System.out.printf("field name:%s,filed value:%s\n" ,field.getName(),field.get(model)); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } System.out.printf("model: %s\n" ,model); } }
直接执行的结果是 Exception
1 java.lang.IllegalAccessException: class com.qxdn.App cannot access a member of class com.qxdn.model.StringModel with modifiers "private"
这是说明我们没有权限去访问私有成员,稍微修改一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void test1 () { StringModel model = new StringModel (); model.setId(1 ); model.setName("qxdn" ); model.setChoice("123" ); System.out.printf("model: %s\n" ,model); Field[] fields = model.getClass().getDeclaredFields(); for (Field field:fields){ field.setAccessible(true ); try { System.out.printf("field name:%s,filed value:%s\n" ,field.getName(),field.get(model)); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } System.out.printf("model: %s\n" ,model); }
运行结果如下
1 2 3 4 model: model {id=1,name=qxdn,choice=123} field name:name,filed value:qxdn field name:choice,filed value:123 model: model {id=1,name=qxdn,choice=123}
可以看到这里 field 没有父类的属性,这是因为 getDeclaredFields()
只会获取当前 class 的属性,不包含父类,因此我们需要自定义函数获取包含父类属性在内的所有属性。因此添加 getAllFields
函数获取包含父类的所有 field
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 public class App { public static void main ( String[] args ) { test1(); } public static List<Field> getAllFields (Class clazz) { if (clazz == null ) { return Collections.emptyList(); } List<Field> result = new ArrayList <>(getAllFields(clazz.getSuperclass())); List<Field> fields = Arrays.stream(clazz.getDeclaredFields()) .collect(Collectors.toList()); result.addAll(fields); return result; } public static void test1 () { StringModel model = new StringModel (); model.setId(1 ); model.setName("qxdn" ); model.setChoice("123" ); System.out.printf("model: %s\n" ,model); List<Field> fields = getAllFields(model.getClass()); for (Field field:fields){ field.setAccessible(true ); try { System.out.printf("field name:%s,filed value:%s\n" ,field.getName(),field.get(model)); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } System.out.printf("model: %s\n" ,model); } }
输出结果
1 2 3 4 5 model: model {id=1,name=qxdn,choice=123} field name:id,filed value:1 field name:name,filed value:qxdn field name:choice,filed value:123 model: model {id=1,name=qxdn,choice=123}
通过反射检测非空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static boolean checkNonNull () { StringModel model = new StringModel (); model.setId(1 ); model.setChoice("123" ); System.out.printf("model: %s\n" , model); List<Field> fields = getAllFields(model.getClass()); for (Field field : fields) { field.setAccessible(true ); try { if (null == field.get(model)) { System.out.printf("field \"%s\" is null\n" , field.getName()); return false ; } } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } System.out.printf("model: %s\n" , model); return true ; }
输出
1 2 model: model {id=1 ,name=null ,choice=123 } field "name" is null
反射来进行类型转换
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 public class InfoUtils { private static final String splitText = "┋" ; public static String encodeMultiChoiceForm (List<MultiChoiceForm> list) { return encodeMultiChoiceForm(list,splitText); } public static String encodeMultiChoiceForm (List<MultiChoiceForm> list,String encoderText) { StringJoiner joiner = new StringJoiner (encoderText); for (MultiChoiceForm form:list){ if (form.getSelected()){ joiner.add(form.getLabel()); } } return joiner.toString(); } public static List<MultiChoiceForm> decodeMultiChoiceForm (String raw) { return decodeMultiChoiceForm(raw,splitText); } public static List<MultiChoiceForm> decodeMultiChoiceForm (String raw,String decoderText) { List<MultiChoiceForm> forms = new ArrayList <>(); String[] strs = raw.split(decoderText); for (String str:strs){ forms.add(new MultiChoiceForm (str,true )); } return forms; } }
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 public static void convert () { StringModel model = new StringModel (); UpdateForm form = new UpdateForm (); form.setId(1 ); form.setName("abc" ); List<MultiChoiceForm> list = new ArrayList <>(); list.add(new MultiChoiceForm ("1" , true )); list.add(new MultiChoiceForm ("2" , false )); list.add(new MultiChoiceForm ("3" , true )); form.setChoice(list); String skipName = "id" ; System.out.printf("model: %s\n" , model); System.out.printf("form: %s\n" , form); List<Field> fields = getAllFields(form.getClass()); for (Field field : fields) { if (field.getName().equals(skipName)){ continue ; } field.setAccessible(true ); try { if (field.getType().equals(String.class) && !StrUtil.isBlankIfStr(field.get(form))) { Field modelField = model.getClass().getDeclaredField(field.getName()); modelField.setAccessible(true ); modelField.set(model, field.get(form)); } else if (null != field.get(form) && !field.getType().equals(String.class)) { Field modelField = model.getClass().getDeclaredField(field.getName()); modelField.setAccessible(true ); if (field.getType().equals(List.class)) { modelField.set(model, InfoUtils.encodeMultiChoiceForm((List<MultiChoiceForm>) field.get(form))); } else { modelField.set(model, field.get(form)); } } } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) { e.printStackTrace(); } } System.out.printf("model: %s\n" , model); System.out.printf("form: %s\n" , form); }
输出
1 2 3 4 model: model {id=null,name=null,choice=null} form: UpdateForm(id=1, name=abc, choice=[MultiChoiceForm(label=1, selected=true), MultiChoiceForm(label=2, selected=false), MultiChoiceForm(label=3, selected=true)]) model: model {id=null,name=abc,choice=1┋3} form: UpdateForm(id=1, name=abc, choice=[MultiChoiceForm(label=1, selected=true), MultiChoiceForm(label=2, selected=false), MultiChoiceForm(label=3, selected=true)])
后记
本来是只知道 java 有反射功能,通常用来做框架,倒是不怎么用。这次在类属性多了之后,且要复杂处理的时候才发现反射的好处。