我有一个HashMap,里面有大约500个键值对。这些值将被设置为对象的属性,所述对象的示例如下 -
public class SomeClass {
private String attrib1;
private double attrib2 = Double.NaN
//getters and setters
}
我必须根据常量从HashMap中提取值,然后将它们设置为此对象。现在,这就是我这样做的方式
public void someMethod(HashMap<String, String> mapToUse, SomeClass some) {
some.setAttrib1(mapToUse.get(MyConstant.SOME_CONST));
some.setAttrib2(methodToParseDouble(mapToUse.get(MyConstant.SOME_CONST2)));
}
这段代码工作正常没有问题,但在我的情况下,我在Map中有500个键值对,该对象包含大约280个属性。所以在代码中有280个硬编码的setter看起来很难看。有更好的方式来做到这一点吗?
现在我的代码有280个setter方法调用,对于每个我有280个键(定义为常量),我用它来查找属性。
我读到了关于BeanUtils的内容,但我正在努力让它与HashMap一起工作。如果你们中的任何一个人都有一个我可以用来从HashMap中提取和设置的示例代码,那就太好了。
编辑:
所以我让BeanUtils工作,但现在我有另一个问题。 BeanUtils工作代码
testMap.put("attrib1", "2");
testMap.put("attrib2", "3");
testMap.put("completelyDiffAttrib1", "10000"); //This breaks the code
SomeClass testBean = new SomeClass();
BeanUtils.populate(testBean, testMap);
上面的代码在我拥有我的Object中的Map中提到的所有属性时起作用,但是如果我在HashMap中有额外的值,这在类中不作为属性出现,那么我的代码会中断。我发现NoClassDef发现错误 -
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.FastHashMap
我已将commons-collections4-4.3.jar添加到类路径中,这在别处提到过。
我可以想到一种方法,我可以先将Map过滤掉,然后通过populate运行它,但我正在寻找更好的方法来实现它。
我无法改变源的方式,即它将成为一个HashMap,我需要它以对象的确切形式。我没有想法,如果有人有任何建议,我可以做一些阅读。谢谢!
一个起点可能是
static final Map<Class<?>, Function<String, Object>> FUNCTION_MAP = new HashMap<>();
static {
FUNCTION_MAP.put(String.class, s -> s);
FUNCTION_MAP.put(Float.class, s -> Float.parseFloat(s));
FUNCTION_MAP.put(Double.class, s -> methodToParseDouble(s));
}
static void someMethod(
final Map<String, String> mapToUse,
final SomeClass some
) throws InvocationTargetException, IllegalAccessException {
// Extract all the methods of SomeClass
final Method[] methods = some.getClass().getDeclaredMethods();
for (final Method method : methods) {
// Consider only methods which are public (setters)
if (!Modifier.isPublic(method.getModifiers())) {
continue;
}
final String name = method.getName();
// Check if it is a setter or not
if (!name.startsWith("set")) {
continue;
}
// Extract the name of the attribute to set (e.g. setAttrib1 -> Attrib1)
final String[] key = name.split("set");
// Extract the single argument type of the setter (String, Double, Float, etc.)
final Class<?> parameterType = method.getParameterTypes()[0];
// Select the right converter (specified inside FUNCTION_MAP) for the argument type
final Function<String, Object> converter = FUNCTION_MAP.get(parameterType);
// Invoke the method, applying the converter on the Map value associated
// to the attribute name (e.g. key[1] = Attrib1)
method.invoke(some, converter.apply(mapToUse.get(key[1])));
}
}
这不需要外部依赖。
使用反射。
以下是使用反射的次优示例解决方案:
public class Main
{
public static class BlammyOne
{
private String propertyDuece;
private String propertyTree;
private String propertyUno;
public String getPropertyDuece()
{
return propertyDuece;
}
public String getPropertyTree()
{
return propertyTree;
}
public String getPropertyUno()
{
return propertyUno;
}
public void setPropertyDuece(
final String newValue)
{
propertyDuece = newValue;
}
public void setPropertyTree(
final String newValue)
{
propertyTree = newValue;
}
public void setPropertyUno(
final String newValue)
{
propertyUno = newValue;
}
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
builder.append("Uno: ");
builder.append(propertyUno);
builder.append(", Duece: ");
builder.append(propertyDuece);
builder.append(", Tree: ");
builder.append(propertyTree);
return builder.toString();
}
}
public static class BlammyTwo
{
private String propertyFive;
private String propertyFour;
private String propertyUno;
public String getPropertyFive()
{
return propertyFive;
}
public String getPropertyFour()
{
return propertyFour;
}
public String getPropertyUno()
{
return propertyUno;
}
public void setPropertyFive(
final String newValue)
{
propertyFive = newValue;
}
public void setPropertyFour(
final String newValue)
{
propertyFour = newValue;
}
public void setPropertyUno(
final String newValue)
{
propertyUno = newValue;
}
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
builder.append("Uno: ");
builder.append(propertyUno);
builder.append(", Four: ");
builder.append(propertyFour);
builder.append(", Five: ");
builder.append(propertyFive);
return builder.toString();
}
}
public static void main(
final String[] arguments)
{
final Map<String, String> valueMap = new HashMap<>();
final BlammyOne blammyOne = new BlammyOne();
final BlammyTwo blammyTwo = new BlammyTwo();
valueMap.put("propertyUno",
"valueUno");
valueMap.put("propertyDuece",
"valueDuece");
valueMap.put("propertyTree",
"valueTree");
valueMap.put("propertyFour",
"valueFour");
valueMap.put("propertyFive",
"valueFive");
settyBetty(valueMap,
blammyOne);
settyBetty(valueMap,
blammyTwo);
System.out.println("blammyOne: " + blammyOne);
System.out.println("blammyTwo: " + blammyTwo);
}
private static void settyBetty(
final Map<String, String> valueMap,
final Object target)
{
final java.lang.reflect.Field[] declaredFieldsArray;
try
{
declaredFieldsArray = target.getClass().getDeclaredFields();
for (java.lang.reflect.Field currentField : declaredFieldsArray)
{
final String fieldValue = currentField.getName();
final PropertyDescriptor propertyDescriptor;
final java.lang.reflect.Method writeMethod;
propertyDescriptor = new PropertyDescriptor(
currentField.getName(),
target.getClass());
writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(target,
fieldValue);
}
}
catch (final SecurityException exception)
{
System.out.println("SecurityException: " + exception);
}
catch (final IntrospectionException exception)
{
System.out.println("IntrospectionException: " + exception);
}
catch (IllegalAccessException exception)
{
System.out.println("IllegalAccessException: " + exception);
}
catch (IllegalArgumentException exception)
{
System.out.println("IllegalArgumentException: " + exception);
}
catch (InvocationTargetException exception)
{
System.out.println("InvocationTargetException: " + exception);
}
}
}