简体   繁体   English

实现通用解析器类

[英]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创建一个ParserStudent 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类,我有两个问题:

  1. How can I pass class and setter methods to parser class? 如何将类和设置方法传递给解析器类?
  2. How to new a generic class (call its constructor) and set its field using the mentioned setter? 如何新建一个泛型类(调用其构造函数)并使用提到的setter设置其字段?

The Function you are passing is not compatible with the Map's declaration. 您传递的FunctionMap'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调用中获取Objectextends 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM