Impelement是一个通用的解析器类

时间:2017-10-19 05:52:28

标签: java parsing generics functional-programming java-8

我想写一个通用的excel解析器类。但是将函数传递给它会有问题。

解析器读取excel行并从每行创建一个对象。通过系统视图,它获得excel文件,类和每列与对象字段(其setter函数)之间的映射,并生成对象列表。

如果我们为每个pojos创建ParserStudentParser pojo为StudentSchoolParserSchool,则会有很多重复解析器。因此,需要实现通用的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类的问题:

  1. 如何将class和setter方法传递给解析器类?
  2. 如何使用提到的setter创建泛型类(调用其构造函数)并设置其字段?

2 个答案:

答案 0 :(得分:2)

您传递的FunctionMap'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, . . .>