Извлечь данные из HashMap и установить атрибуты в объекте - PullRequest
0 голосов
/ 05 марта 2019

У меня есть 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)));
}

Этот код прекрасно работает без проблем, но в моем случае у меня есть 500 пар ключ-значение на карте, и объект содержит около 280 атрибутов.Таким образом, наличие 280 жестко запрограммированных сеттеров выглядит ужасно в коде.Есть ли лучший элегантный способ сделать это?

Прямо сейчас мой код имеет 280 вызываемых методов установки, и для каждого из них у меня есть 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);

Приведенный выше код работает, когда у меня есть все атрибуты, упомянутые в карте в моем объекте, но если у меня есть дополнительное значение в 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-collection4-4.3.jar в путь к классам, о котором упоминалось в другом месте.

Я могу вспомнить один подход, в котором я могу сначала отфильтровать Карту, а затем запустить ее через заполнение, но я ищу более эффективные способы сделать это.


Я не могуизменить, как источник, то есть это будет HashMap, и мне нужно именно в этой форме объекта.У меня нет идей, если у кого-то есть предложения, я могу немного почитать.Спасибо!

Ответы [ 2 ]

1 голос
/ 05 марта 2019

Использовать отражение.

Вот пример неоптимального решения, в котором используется отражение:

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);
    }
  }
}
1 голос
/ 05 марта 2019

Начальная точка может быть

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])));
    }
}

Это не требует внешних зависимостей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...