【问题标题】:Instantiate a generic variable using string class name使用字符串类名实例化泛型变量
【发布时间】:2015-11-26 09:33:15
【问题描述】:

我正在使用谷歌的 CSVReader,它需要一个类名来创建解析器。使用解析器,我正在将 CSV 文件读入列表。

考虑这段代码:

ValueProcessorProvider provider = new ValueProcessorProvider();
    CSVEntryParser<A> entryParser = new AnnotationEntryParser<A>(A.class, provider);

    CSVReader<A> newExternalFileCSVReader = 
            new CSVReaderBuilder<A>(m_NewExternalFile).entryParser((CSVEntryParser<A>) entryParser).strategy(new CSVStrategy(',', '"', '#', true, true)).build();
    List<A> m_NewExternalFileData = newExternalFileCSVReader.readAll();

使用此代码,我可以读取特定于 A 类的 CSV 文件。
我还有其他几个类:B、C、D,它们都使用上面相同的代码,只是它们各自的类。

是否有一个函数可以将类名作为字符串传递,它可以根据字符串输入名称实例化 CSVReader/解析器?在哪里不必创建 3 个不同的代码部分(对于 B、C、D 类),我可以使用同一个,只需输入相关的类名?

【问题讨论】:

  • A、B、C 和 D 在任何方面是否相关或相似?

标签: java csv generics


【解决方案1】:

您可以使用工厂模式。

创建一个接口并在 A、B、C 和 D 的基本方法中定义。

那么所有 A、B、C 和 D 类都必须实现该接口。

public interface BaseInterface {
    // your methods
}

然后创建一个工厂类,在其中传递一个标识符,它将返回正确启动的阅读器

package a;

public final class Factory {

    // Not instantiable
    private Factory() {
        throw new AssertionError("Not instantiable");
    }

    public static CSVReader<your interface> getReader(String reader) {

        if ("A".equals(reader)) {
            return new CSVReader<A>();
        } else if ("B".equals(reader)) {
            return new CSVReader<B>();
        }
        // TODO create all your readers
    }
}

现在,您可以像这样通过您的工厂类调用阅读器:

ValueProcessorProvider provider = new ValueProcessorProvider();
    CSVEntryParser<A> entryParser = new AnnotationEntryParser<A>(A.class, provider);

    CSVReader<your interface> newExternalFileCSVReader = 
            Factory("your reader type");
    List<your interface> m_NewExternalFileData = newExternalFileCSVReader.readAll();

由于您没有发布 A、B、C 和 D 类,您必须自定义该代码,但按照这种方式,我认为您可以完成您想要的。

【讨论】:

    【解决方案2】:

    你可以这样做:

    public class MyCSVReader<T> {
    
        private Class<T> clazz;
    
        public MyCSVReader(Class<T> clazz) {
            this.clazz = clazz;
        }
    
        public List<T> readData(File file) {
            ValueProcessorProvider provider = new ValueProcessorProvider();
            CSVEntryParser<T> parser = new AnnotationEntryParser<T>(clazz, provider);
            CSVStrategy strategy = new CSVStrategy(',', '"', '#', true, true);
            CSVReaderBuilder builder = new CSVReaderBuilder<T>(file);
            CSVReader<T> reader = builder.entryParser(parser ).strategy(strategy).build();
            return reader.readAll();
        }
    }
    

    那么你会这样做:

    MyCSVReader<A> readerA = new MyCSVReader<>(A.class);
    List<A> data = readerA.readData(m_NewExternalFile);
    

    对于任何其他类也是如此。

    编辑:也许按文件扩展名查找类型会很有用?

    public class MyCSVReaderFactory {
    
        private static Map<String, MyCSVReader<?>> readersByFileExtension = new HashMap<>();
    
        // files with data for class A have extension .xta, etc.
        static {
            readersByFileExtension.put(".xta", new MyCSVReader<>(A.class));
            readersByFileExtension.put(".xtb", new MyCSVReader<>(B.class));
        }
    
        public MyCSVReader<?> create(String fileExtension) {
            MyCSVReader<?> reader = readersByFileExtension.get(fileExtension);
            if (reader == null) {
                throw new IllegalArgumentException("Unknown extension: " + fileExtension);
            }
            return reader;
         }
    }
    
    public List<?> doStuff(File file) {
        String fileExtension = getFileExtension(file);
        MyCSVReader<?> reader = MyCSVReaderFactory.create(fileExtension);
        return reader.readAll();
    }
    
    private String getFileExtension(File file) { 
        // TODO: implement this
    }
    

    如果您不想要一个列表(对象),那么 A-D 类应该实现一个公共接口或扩展一个可用于泛化的公共超类。

    【讨论】:

    • 我认为您错过了更正以下代码,CSVReaderBuilder builder = new CSVReaderBuilder(file); //它仍然引用A类
    • 如果这回答了您的问题,请接受答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 2013-10-03
    相关资源
    最近更新 更多