我想写一个通用的excel解析器类。但是将函数传递给它会有问题。
解析器读取excel行并从每行创建一个对象。通过系统视图,它获得excel文件,类和每列与对象字段(其setter函数)之间的映射,并生成对象列表。
如果我们为每个pojos创建Parser
(StudentParser
pojo为Student
,SchoolParser
为School
,则会有很多重复解析器。因此,需要实现通用的Parser
类,但我不知道将setter方法传递给类。我们需要这样的东西:
private Map<Integer, java.util.function.Consumer> mapColumnIndexToSetter = new HashMap<>();
mapColumnIndexToSetter .put(3, Student::setName);
但Student::setName
收到错误:
Error:(30, 41) java: incompatible types: invalid method reference
incompatible types: java.lang.Object cannot be converted to java.lang.String`
我知道上面的代码确实不能满足我的要求。但它提供了一些关于要求的想法。
我有两个关于在java 8中实现这个通用Parser
类的问题:
答案 0 :(得分:2)
您传递的Function
与Map's
声明不兼容。
在Map
声明中,您错过了为Function
指定泛型类型参数,因此将推断如下:
Function<Object, Object>
另一方面,你正在通过:
// I guess getName() is returning a String
Function<Student, String> function = Student::getName;
现在,除非另有说明,否则此类引用中的泛型类型参数必须完全匹配,如下所示:
Function<? extends Object, ? extends Object>
只要您从Object
来电中获得Function
即可,extends Object
显然毫无意义,因此可以简化为:
Function<?, ?>
但你可以在这里添加一般限制,例如:
Function<?, ? extends Serializable>
答案 1 :(得分:2)
像Student.setName(String)
这样的方法需要两个参数,Student
实例可以调用方法,String
需要传递。BiConsumer<Student,String>
。因此,适当的功能接口是HashMap
。
顺便说一句,excel表的列很少稀疏,因为它们证明了从索引到函数使用 List
是正确的。具有每列功能的Supplier
是直截了当的。
要实例化该类型,您可以使用public class Parser<T> {
public static final BiConsumer<Object,Object> IGNORE = (x,y) -> {};
private final Supplier<? extends T> instantiator;
private final List<BiConsumer<? super T, ? super String>> setters;
public Parser(Supplier<? extends T> instantiator,
List<BiConsumer<? super T, ? super String>> setters) {
this.instantiator = Objects.requireNonNull(instantiator);
this.setters = new ArrayList<>(setters);
if(this.setters.contains(null)) throw new NullPointerException();
}
public Parser(Supplier<? extends T> instantiator,
BiConsumer<? super T, ? super String>... setters) {
this(instantiator, Arrays.asList(setters));
}
// Replace the two-dimensional string array with actual xls parsing...
public List<T> parse(String[][] data) {
List<T> result = new ArrayList<>(data.length);
for(String[] row: data) {
T instance = instantiator.get();
for (int ix = 0; ix < row.length; ix++)
setters.get(ix).accept(instance, row[ix]);
result.add(instance);
}
return result;
}
}
。
把它放在一起,这样的课可能看起来像
Student
然后,要仅使用第3列来初始化其名称来实例化Parser<Student> parser = new Parser<>(Student::new,
Parser.IGNORE, Parser.IGNORE, Student::setName);
List<Student> list = parser.parse(new String[][] {
{null, null, "Moe" },
{null, null, "Larry" },
{null, null, "Curly" },
});
list.forEach(System.out::println);
,您可以使用
<user_id><tab space><friend_1_id,friend_2_id_2, . . .>