我班上有大约20个变量。我需要读取excel表,然后相应地获取数据并根据变量的名称在DTO类中设置它。除2外,所有变量都是字符串。那些2是数字的。现在我将从excel获得单行,接下来我必须获取每个单元格的内容并根据单元格的数据/索引的顺序显式调用setter方法。有没有办法自动化这个?我的意思是任何方法为特定索引调用特定方法?我们可以在某个数组中定义它的关系并将它们联系起来吗?
请帮忙。
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);
}
}我想到了两种不同的解决方案。
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;
}
}第二种解决方案是使用以下模式,使用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;
}
}
我添加了下面的单元测试,它允许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获得
我希望这有帮助。
对于MyObjectImpl
,请参阅answer of @johan-witters。
只需创建一个接受带有值的数组的构造函数。您必须列出字段编号和设置之间的所有映射,但您必须在所有解决方案中执行类似的操作。
public MyObjectImpl(String[] values) {
this.name = values[0];
this.street = values[1];
this.number = Integer.valueOf(values[0]);
// ...
}
有点类似于@ johan-witters的解决方案2,但仅使用标准的Java Functional接口和Method引用,使其更紧凑。
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;
}