欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

动态properties转换

程序员文章站 2022-07-14 14:43:18
...
今天同事和我讨论他遇到的一个问题。具体要求是这样的,在运行时,我们会从模块G得到一个Map,这个Map里面都是一些字符串对,你可以理解成一个字典,有字符串的key和字符串的value。简短节说,就是
Map<String, String>

非常非常复杂深奥。

好,现在我们事先知道要从这个map里读取一些数据点,比如:id, name, sex等等。

对id,我们知道读出来的是int;对name,是string;对sex,应该对应一个叫Gender的enum类型。

这就涉及一个自动类型转换的问题。我们希望不用对每个数据点做手工类型转换。

另外一个需求,一些数据点是有缺省值的。比如name我们可以缺省为空字符串。
这样,如果map里面没有某个值,我们就看缺省值,如果有,就用这个缺省值,如果没有,就抛异常。

手工做的话,大概是这样:
String idValue = map.get("id");
if (idValue == null) {
  throw ...;
}
int id = Integer.parseInt(idValue);

String name = map.get("name");
if (name == null) {
  name = "";
}

String sexValue = map.get("sex");
if (sexValue == null) {
  throw ...;
}
Gender sex = Gender.valueOf(sexValue);
...


比较痛苦。于是做了一个动态代理:
public final class PropertyConverter<T> {
  private final Class<T> targetType;
  
  private PropertyConverter(Class<T> targetType) {...}

  public static <T> PropertyConverter<T> to(Class<T> targetType) {
    return new PropertyConverter<T>(targetType);
  }

  public T from(final Map<String, String> map) {
    return Proxy.newProxyInstance(
      new Class[]{targetType}, targetType.getClassLoader(), new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) {
          String value = map.get(method.getName());
          if (value == null) {
            Object defaultValue = method.getDefaultValue();
            if (defaultValue == null) {
              throw ...;
            }
            return defaultValue;
          }
          return convert(value, method.getReturnType());
        }
    });
  }
}


convert()函数是调用apache的ConvertUtilsBean做的,没什么说的。

那么,用法呢?

@interface Foo {
  int id();
  String name() default "";
  Gender sex();
}

Map<String, String> map = ...;
Foo foo = PropertyConverter.to(Foo.class).from(map);
foo.id();
foo.name();


这里面,对annotation的用法比较特别。不过不这么做,java也不提供一个简单并且类型安全的指定缺省值的方法。当然,如果你凑巧不需要缺省值,那么也不用annotation,直接用interface就好。