[英]Impelement a generic parser class
I wanna to write a generic excel parser class. 我想编写一个通用的excel解析器类。 But there is a problem with passing functions to it.
但是将函数传递给它有一个问题。
A parser read excel rows and create an object from each rows. 解析器读取excel行并从每行创建一个对象。 By a systemic view, it get an excel file, a class and a mapping between each column and an object field (its setter function) and generate a list of objects.
通过系统视图,它将获得一个excel文件,一个类以及每个列与一个对象字段(其setter函数)之间的映射,并生成一个对象列表。
If we create a Parser
for each pojos ( StudentParser
for Student
pojo and SchoolParser
for School
), there is a lot of duplicates in these parsers. 如果我们为每个pojo创建一个
Parser
( Student
StudentParser
用于Student
pojo, SchoolParser
用于School
),则这些解析器中有很多重复项。 So, Implementation of a generic Parser
class is required, but I have no idea for passing setter methods to the class. 因此,需要实现通用
Parser
类,但是我不知道将setter方法传递给该类的想法。 We need something like this: 我们需要这样的东西:
private Map<Integer, java.util.function.Consumer> mapColumnIndexToSetter = new HashMap<>();
mapColumnIndexToSetter .put(3, Student::setName);
But Student::setName
gets an error: 但是
Student::setName
出现错误:
Error:(30, 41) java: incompatible types: invalid method reference
incompatible types: java.lang.Object cannot be converted to java.lang.String`
I know the above code does can not meet my requirements. 我知道上面的代码不能满足我的要求。 But It gives some idea about what is the requirements.
但是它给出了一些关于需求的想法。
I have two questions on implementation of this generic Parser
class in java 8: 关于在Java 8中实现此通用
Parser
类,我有两个问题:
The Function
you are passing is not compatible with the Map's
declaration. 您传递的
Function
与Map's
声明不兼容。
At the Map
declaration you missed to specify generic type arguments for Function
, so those will be inferred as follows: 在
Map
声明中,您没有为Function
指定通用类型参数,因此可以如下推断:
Function<Object, Object>
On the other hand you are passing: 另一方面,您正在通过:
// I guess getName() is returning a String
Function<Student, String> function = Student::getName;
Now generic type arguments in such references have to match exactly unless specified otherwise, which would look like: 现在,除非另外指定,否则此类引用中的泛型类型参数必须完全匹配,如下所示:
Function<? extends Object, ? extends Object>
And as long as you are fine with getting Object
from the Function
call, extends Object
is obviously pointless, so it can be reduced to: 只要可以从
Function
调用中获取Object
, extends Object
显然毫无意义,因此可以简化为:
Function<?, ?>
But you might as well add a generic restriction here, such as: 但是您最好在此处添加一个通用限制,例如:
Function<?, ? extends Serializable>
A method like Student.setName(String)
requires two arguments, the Student
instance to invoke the method on and the String
to be passed. 像
Student.setName(String)
这样的方法需要两个参数, Student
实例用于调用该方法,而String
传递。 Therefore, an appropriate functional interface would be BiConsumer<Student,String>
. 因此,适当的功能接口应为
BiConsumer<Student,String>
。
By the way, the columns of an excel table are rarely that sparse that they justify the use of a HashMap
from index to function. 顺便说一句,excel表的列很少稀疏到可以证明从索引到函数使用
HashMap
是合理的。 A List
having a function for each column is straight-forward. 具有针对每一列的功能的
List
是直接的。
To instantiate the type, you may use a Supplier
. 要实例化类型,可以使用
Supplier
。
Putting it together, such a class may look like 放在一起,这样的类可能看起来像
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;
}
}
Then, to instantiate Student
s only using the 3rd column to initialize their names, you can use 然后,要仅使用第三列初始化学生的姓名来实例化
Student
,可以使用
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.