bizObjects.add(map2Bean(mapForKey, bizObjects.getObjectClass()));
}
);
return this;
}
public List<ItemCore> buildFinalList() {
Map<String, List<ItemCore>> itemCores = objMapping.get("item_core").getObjects();
List<ItemCore> finalItemCoreList = new ArrayList<>();
itemCores.forEach(
(itemCoreId, itemCoreList) -> {
ItemCore itemCore = itemCoreList.get(0);
itemCore.setItem((Item) objMapping.get("item").getSingle(itemCoreId));
itemCore.setItemPrice((ItemPrice) objMapping.get("item_price").getSingle(itemCoreId));
itemCore.setItemPriceChangeLogs(objMapping.get("item_price_change_log").get(itemCoreId));
finalItemCoreList.add(itemCore);
}
);
return finalItemCoreList;
}
}
BizObjects.java
package zzz.study.algorithm.object;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class BizObjects<T, K> {
private Class<T> cls;
private Map<K, List<T>> map;
private Function<T, K> keyFunc;
public BizObjects(Class<T> cls, Map<K,List<T>> map, Function<T,K> keyFunc) {
this.cls = cls;
this.map = (map != null ? map : new HashMap<>());
this.keyFunc = keyFunc;
}
public void add(T t) {
K key = keyFunc.apply(t);
List<T> objs = map.getOrDefault(key, new ArrayList<>());
objs.add(t);
map.put(key, objs);
}
public Class<T> getObjectClass() {
return cls;
}
public List<T> get(K key) {
return map.get(key);
}
public T getSingle(K key) {
return (map != null && map.containsKey(key) && map.get(key).size() > 0) ? map.get(key).get(0) : null;
}
public Map<K, List<T>> getObjects() {
return Collections.unmodifiableMap(map);
}
}
新的实现的主要特点在于:
- 去掉了条件语句;
- 将转换为嵌套对象的重要配置与逻辑都集中到 objMapping ;
- 更加对象化的思维。
美中不足的是,大量使用了泛型来提高通用性,同时也牺牲了运行时安全的好处(需要强制类型转换)。 后半段关联对象,还是不够配置化,暂时没想到更好的方法。
为什么 BizObjects 里要用 Map 而不用 List 来表示多个对象呢 ? 因为后面需要根据 itemCoreId 来关联相应对象。如果用 List , 后续还要一个单独的 buildObjMap 操作。这里添加的时候就构建 Map ,将行为集中于 BizObjects 内部管理, 为后续配置化地关联对象留下一个空间。
一个小坑
运行结果会发现,转换后的 item 对象的属性 sId, gId 的值为 null 。纳尼 ? 这是怎么回事呢?
单步调试,运行后,会发现在 BeanUtilsBean.java 932 行有这样一行代码(用的是 commons-beanutils 的 1.9.3 版本):
PropertyDescriptor descriptor = null;
try {
descriptor =
getPropertyUtils().getPropertyDescriptor(target, name);
if (descriptor == null) {
return; // Skip this property setter
}
} catch (final NoSuchMethodException e) {
return; // Skip this property setter
}
当 name = “gId” 时,会获取不到 descriptor 直接返回。 为什么获取不到呢,因为 Item propertyDescriptors 缓存里的 key是 GId ,而不是 gId !
为什么 itemPropertyDescriptors 里的 key 是 GId 呢? 进一步跟踪到 propertyDescriptors 的生成,在 Introspector.getTargetPropertyInfo 方法中,是根据属性的 getter/setter 方法来生成 propertyDescriptor 的 name 的。 最终定位的代码是 Introspector.decapitalize 方法:
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0]