Java 推动通用解析器类

Java 推动通用解析器类,java,parsing,generics,functional-programming,java-8,Java,Parsing,Generics,Functional Programming,Java 8,我想写一个通用的excel解析器类。但是向它传递函数有一个问题 解析器读取excel行并从每行创建一个对象。通过系统视图,它获得一个excel文件、一个类以及每个列和一个对象字段(其setter函数)之间的映射,并生成一个对象列表 如果我们为每个pojo创建一个Parser(StudentParserforStudentpojo和SchoolParserforSchool),这些解析器中会有很多重复项。因此,需要实现一个通用的解析器类,但我不知道如何将setter方法传递给该类。我们需要这样的东

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

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

如果我们为每个pojo创建一个
Parser
StudentParser
for
Student
pojo和
SchoolParser
for
School
),这些解析器中会有很多重复项。因此,需要实现一个通用的
解析器
类,但我不知道如何将setter方法传递给该类。我们需要这样的东西:

private Map<Integer, java.util.function.Consumer> mapColumnIndexToSetter = new HashMap<>();
mapColumnIndexToSetter .put(3, Student::setName);
我知道上面的代码不能满足我的要求。但是它给出了一些关于什么是需求的想法

关于在java 8中实现这个通用的
解析器
类,我有两个问题:

  • 如何将类和setter方法传递给解析器类
  • 如何新建泛型类(调用其构造函数)并使用所提到的setter设置其字段

  • 您传递的
    函数
    映射的
    声明不兼容

    映射
    声明中,您没有指定
    函数
    的泛型类型参数,因此这些参数将推断如下:

    Function<Object, Object>
    
    但您也可以在此处添加一个通用限制,例如:

    Function<?, ? extends Serializable>
    

    Function像
    Student.setName(String)
    这样的方法需要两个参数,调用方法的
    Student
    实例和要传递的
    String
    。因此,合适的功能接口应该是
    BiConsumer

    顺便说一句,excel表的列很少如此稀疏,以至于它们可以从索引到函数使用
    HashMap
    。每个列都有一个函数的
    列表是直接的

    要实例化该类型,可以使用
    供应商

    把它放在一起,这样一个类可能看起来像

    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;
        }
    }
    

    我很好奇在
    映射
    中存储之后如何使用
    函数
    。对不起,这是
    setName(String name)
    的错误,而不是
    getName()
    。你回答了我两个问题中的哪一个?他编辑了这个问题吗?
    映射是
    整数
    消费者
    ,而不是
    函数
    。是的,我编辑文本:
    getName()
    -->
    setName
    函数
    到-->
    消费者
    (因为setter没有任何输出)。但这些更改不会影响我的主要问题我实现了这一点,但当我在非静态方法中实例化
    解析器时,它在
    Student::setName
    上出现以下错误:
    java:无效的方法引用非静态方法setName()无法从静态上下文引用
    是否确定“非静态方法”当错误消息显示“来自静态上下文”时?检查您的代码在哪些方面不同于…是的,我在静态上下文(Main)和非静态上下文(公共非静态类)中尝试了两次。有时,编译器喜欢给出隐藏实际问题的虚假错误。请检查所有使用的类型是否正确导入,以及是否忘记了
    ,等等,并与我在前面的注释中给出的链接进行比较。无法统一处理任意类型的函数。您可以让
    解析器接受arbi使用
    BiConsumer
    的三元函数,但如果没有未选中的操作(例如
    ((BiConsumer)c)。accept(anObject,aValue),则无法调用其
    accept
    方法
    ,因为无法在编译时证明XLS单元格的类型与setter的参数类型匹配。您只能尝试通过将列设置封装在专用类中来尽早发现错误,即,
    包含预期类型(
    )和setter(
    双消费者)
    
    Function<? extends Object, ? extends Object>
    
    Function<?, ?>
    
    Function<?, ? extends Serializable>
    
    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;
        }
    }
    
    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);