简体   繁体   中英

Instantiate a generic variable using string class name

I'm using google's CSVReader which requires a class name to create a parser. Using the parser, I'm reading a CSV file into a list.

Consider this code:

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();

With this code, I can read a CSV file that is specific to class A.
I have several other classes: B,C,D, which all uses the same code above, just with their respective class.

Can there be a function where I'll pass the class name as String which can instantiate a CSVReader / parser's based on the String input name? where instead of having to create 3 different code sections (for classes B,C,D), I can use the same one, just input the relevant class name?

You can use a factory pattern.

Create an interface and define inside the base methods for A, B, C and D.

Then all A, B, C and D classes must implements that interface.

public interface BaseInterface {
    // your methods
}

Then create a Factory class in which you pass an identifier and it will return your reader properly initiated

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

Now, you can call the reader through your factory class like this:

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();

As you did not post the A, B, C and D classes you have to customize that code, but following that way I think you can accomplish what you want.

You could do this:

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

Then you would do:

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

And the same for any other classes.

EDIT: Maybe it would be useful to lookup the type by file extension?

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
}

If you don't want a List (Objects) then classes AD should implement a common interface or extend a common superclass which can be used to generalize.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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