我的类中有大约 20 个变量。我需要读取 Excel 工作表,然后获取相应的数据,并根据变量的名称将其设置在 DTO 类中。 除 2 外,所有变量都是字符串。这 2 个是数字。 现在我将从 Excel 中获取单行,接下来我必须获取每个单元格的内容并根据单元格的数据/索引的顺序显式调用 setter 方法。有什么办法可以自动化执行此操作吗? 我的意思是有什么方法可以为特定索引调用特定方法吗?我们可以在数组中的某个地方定义它的关系并将它们关联起来吗?
请帮忙。
最佳答案
假设
假设有 3 个变量,而不是 20 个变量。
假设您的 Excel 工作表包含以下列:
- 第 1 列 = 名称
- 第 2 列 = 街道
- 第 3 列 = 数字。
假设以下是您的类(class):
包 com.johanw.stackoverflow.dynamicinit;
公共(public)类MyObjectImpl { 私有(private)字符串名称; 私有(private)弦街; 私有(private)整数;
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); }
}
我想到了两种不同的解决方案。
解决方案1
第一个解决方案是使用反射和按照您想要初始化对象的顺序排序的方法名称列表,如下面的代码所示...
包 com.johanw.stackoverflow.dynamicinit;
导入java.lang.reflect.InitationTargetException; 导入java.lang.reflect.Method;
公共(public)类 PopulateObjects2 { 接口(interface)InitialiseFieldAction { 无效初始化(MyObjectImpl 对象,字符串值); }
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
第二个解决方案是使用以下模式,使用 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);
}
}
推荐
我建议使用第二种方法,即不使用反射。但是,您可能有理由希望能够使用反射,以防列不固定,并且您需要从某些配置或电子表格的标题中检索这些值。
代码
下面的代码/项目可在https://github.com/johanwitters/stackoverflow-dynamicinit获取
我希望这会有所帮助。
关于java - java中如何根据输入数据顺序/名称动态调用不同的setter方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48064761/