I’m trying to create a simple class to read a csv file and store the contents in an
ArrayList<ArrayList<T>>.
I’m creating a generic class CsvReader so that I can handle data of different types: int, double, String. If I had, say, a csv file of doubles, I was imagining I would use my class like this:
//possible method 1
CsvReader<Double> reader = new CsvReader<Double>();
ArrayList<ArrayList<Double>> contents = reader.getContents();
//possible method 2
CsvReader reader = new CsvReader(Double.class);
ArrayList<ArrayList<Double>> contents = reader.getContents();
But method 1 won’t work since type erasure prevents you from writing code like
rowArrayList.add(new T(columnStringValue));
But I can’t even make the passing in a Double.class solution work. The problem is that what’s really going on is that I need my class “parameterized” (in the general sense of that word, not the technical java generics sense) on a type with the following property: it has a ctor accepting a single String argument. That is, to create the row ArrayLists on, say, a Double csv file, I’d need to write:
StringTokenizer st = new StringTokenizer(line,",");
ArrayList<Double> curRow = new ArrayList<Double>();
while (st.hasMoreTokens()) {
curRow.add(new Double(st.nextToken());
}
Having passed in Double.class, I could get its String ctor using
Constructor ctor = c.getConstructor(new Class[] {String.class});
but this has two problems. Most importantly, this is a general constructor that will return a type Object, which I cannot then downcast into a Double. Second, I would be missing “type” checking on the fact that I am requiring my passed in class to have a String arg constructor.
My question is: How can I properly implement this general purpose CsvReader?
Thanks,
Jonah
I’m not sure a generic CSV reader would be this simple to use (and to create, by the way).
The first question that comes to my mind is: What if the CSV contains three columns: first an integer, then a string and finally a date? How would you use your generic CSV reader?
Anyway, lets suppose you want to create a CSV reader where all columns are of the same type. As you said, you can’t parametrize a class on a type “that accepts a
Stringas constructor”. Java just doesn’t allow that. The solution using reflection is a good start. But what if your class doesn’t take aStringas parameter in one of its constructors?Here you can come with an alternative: a parser that would take your String and return an object of the correct type. Create a generic interface, and make some implementations for the type you want to crawl:
And then, implement:
Then, you CSV reader can take a
Parseras one of its parameters. Then, it can use this parser to convert eachStringinto a Java object.With this solution, you get rid of the not-so-pretty reflection your where using. And you can convert to any type, you just have to implement a
Parser.Your reader will look like this:
Now, back at the problem where a CSV file can have multiple types, just improve your reader a little. All you need is a list of parsers (one per column) instead of one that parse all columns.
Hope that helps 🙂