如何根据java中的输入数据顺序/名称动态调用不同的setter方法?

时间:2018-01-02 16:47:02

标签: java web-services apache-poi

我班上有大约20个变量。我需要读取excel表,然后相应地获取数据并根据变量的名称在DTO类中设置它。 除2外,所有变量都是字符串。那些2是数字的。 现在我将从excel获得单行,接下来我必须获取每个单元格的内容并根据单元格的数据/索引的顺序显式调用setter方法。有没有办法自动化这个? 我的意思是任何方法为特定索引调用特定方法?我们可以在某个数组中定义它的关系并将它们联系起来吗?

请帮忙。

2 个答案:

答案 0 :(得分:1)

假设

  1. 假设3,而不是20个变量。

  2. 假设您的Excel工作表包含以下列:

    • 第1列=姓名
    • 第2栏=街道
    • 第3列=数字。
  3. 假设下面是你的那个类:

    package com.johanw.stackoverflow.dynamicinit;

    public class MyObjectImpl {     私有字符串名称;     私人弦街;     private int number;

    public MyObjectImpl() {
    }
    
    public MyObjectImpl(String name, String street, int number) {
        this.name = name;
        this.street = street;
        this.number = number;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getStreet() {
        return street;
    }
    
    public void setStreet(String street) {
        this.street = street;
    }
    
    public int getNumber() {
        return number;
    }
    
    public void setNumber(int number) {
        this.number = number;
    }
    
    public void setNumberAsString(String number) {
        this.number = Integer.valueOf(number);
    }
    

    }

  4. 我想到了两种不同的解决方案。

    解决方案1 ​​

    1. 第一个解决方案是使用反射和按照要初始化对象的顺序排序的方法名称列表,如下面的代码所示......

      package com.johanw.stackoverflow.dynamicinit;

      import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;

      public class PopulateObjects2 {     接口InitialiseFieldAction {         void initialise(MyObjectImpl object,String value);     }

      private static String[] initMethods = {
              "setName", "setStreet", "setNumberAsString"
      };
      
      private static Method getMethod(int index) throws NoSuchMethodException {
          if ((index < 0) || (index > initMethods.length - 1)) return null;
          Class clazz = MyObjectImpl.class;
          return clazz.getMethod(initMethods[index], String.class);
      }
      
      public static MyObjectImpl retrieveWithValues(String[] values) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
          if (values.length != initMethods.length) return null;   // Handle the fact you don't have the same amount of initialisations as the amount of fields
          MyObjectImpl o = new MyObjectImpl();
          for (int i = 0; i < initMethods.length; i++) {
              Method m = getMethod(i);
              m.invoke(o, values[i]);
          }
          return o;
      }
      

      }

    2. 解决方案2

      第二种解决方案是使用以下模式,使用InitialiseFieldAction实现列表。请注意,在解决方案2中,我不使用setNumberAsString方法,而是在InitialiseFieldAction的实现中将字符串转换为数字。

      package com.johanw.stackoverflow.dynamicinit;
      
      public class PopulateObjects1 {
      
          interface InitialiseFieldAction {
              void initialise(MyObjectImpl object, String value);
          }
      
          private static InitialiseFieldAction[] initActions = new InitialiseFieldAction[] {
                  new InitialiseFieldAction() { public void initialise(MyObjectImpl o, String value) { o.setName(value);} },
                  new InitialiseFieldAction() { public void initialise(MyObjectImpl o, String value) { o.setStreet(value);} },
                  new InitialiseFieldAction() { public void initialise(MyObjectImpl o, String value) { o.setNumber(Integer.valueOf(value));} },
          };
      
          public static MyObjectImpl retrieveWithValues(String[] values) {
              if (values.length != initActions.length) return null;   // Handle the fact you don't have the same amount of initialisations as the amount of fields
              MyObjectImpl o = new MyObjectImpl();
              for (int i = 0; i < initActions.length; i++) {
                  initActions[i].initialise(o, values[i]);
              }
              return o;
          }
      }
      

      用法

      • 对于工作表中的每一行,创建一个字符串数组,表示从左到右的每列值。
      • 然后调用PopulateObjects1 / 2.retrieveWithValues(该数组)。
      • 它将返回使用该数组初始化的对象,与initActions一起动态配置。在这种情况下,InitialiseFieldAction实现。

      测试

      我添加了以下单元测试,允许a)测试b)了解如何使用代码。

      package com.johanw.stackoverflow.dynamicinit.init;
      
      import org.junit.Assert;
      import org.junit.Test;
      
      import java.lang.reflect.InvocationTargetException;
      import java.time.LocalDate;
      import java.util.Arrays;
      import java.util.Optional;
      import org.junit.Assert;
      import org.junit.Test;
      
      public class PopulateObjects {
          private String[] exampleRow = { "name", "street", "10"};
      
          @Test
          public void method1() {
              MyObjectImpl o = PopulateObjects1.retrieveWithValues(exampleRow);
              Assert.assertTrue(o.getName().equals("name"));
              Assert.assertTrue(o.getStreet().equals("street"));
              Assert.assertTrue(o.getNumber() == 10);
              System.out.println(o);
          }
      
          @Test
          public void method2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
              MyObjectImpl o = PopulateObjects2.retrieveWithValues(exampleRow);
              Assert.assertTrue(o.getName().equals("name"));
              Assert.assertTrue(o.getStreet().equals("street"));
              Assert.assertTrue(o.getNumber() == 10);
              System.out.println(o);
          }
      }
      

      Recommondation

      我建议使用第二种方法,即不使用反射。但是,您可能有理由希望能够在未修复列的情况下使用反射,并且您将从某些配置或电子表格的标题中检索这些值。

      代码

      以下代码/项目位于https://github.com/johanwitters/stackoverflow-dynamicinit

      我希望这会有所帮助。

答案 1 :(得分:1)

对于MyObjectImpl,请参阅answer of @johan-witter s。

解决方案1:构造函数接受数组

只需创建一个接受带有值的数组的构造函数。您必须列出字段编号和设置之间的所有映射,但您必须在所有解决方案中执行类似的操作。

public MyObjectImpl(String[] values) {
    this.name = values[0];
    this.street = values[1];
    this.number = Integer.valueOf(values[0]);
    // ...
}

解决方案2:使用映射器函数定义映射

与@ johan-witters的解决方案2有些相似,但只使用标准的Java 功能接口方法引用,这使它更紧凑。

public static void main(String[] args) {

    String[] values = { /* data from file */};

    Map<Integer, BiConsumer<MyObjectImpl, String>> mappers = new HashMap<>();
    mappers.put(0, MyObjectImpl::setName);
    mappers.put(1, MyObjectImpl::setStreet);
    mappers.put(2, asInt(MyObjectImpl::setNumber));
    // ...

    MyObjectImpl obj = retrieveWithValues(values, mappers);

}

private static BiConsumer<MyObjectImpl, String> asInt(BiConsumer<MyObjectImpl, Integer> intConsumer) {
    return (obj, i) ->  intConsumer.accept(obj, Integer.valueOf(i));
}

private static MyObjectImpl retrieveWithValues(String[] values, Map<Integer, BiConsumer<MyObjectImpl, String>> mappers) {
    if (values.length != mappers.size()) {
        return null;
    }
    MyObjectImpl obj = new MyObjectImpl();
    for (int i = 0; i < mappers.size(); i++) {
        mappers.get(i).accept(obj, values[i]);
    }
    return obj;
}