The goal i am trying to achieve, is to have different FileLoaders like CSVFileLoader, ExcelFileLoader that can load up any object of type T, as long as it know how to convert using ‘C’ and create the object of type T. Hope this makes sense.
I am trying to use generics to create a generic FileLoader that will take a Converter of type C and return a List of Object of type T. So I went about creating something like the below but it inst working as expected.
I am getting an error while trying to return object Transaction in the convert method. How should I rewrite this so that it can use generics and I can improve this code to work. I understand there is type erasure, so that is why its complaning in the below code but not sure how to fix it. Please advise
//FileLoader takes a converter of type C, Object of type T public interface FileLoader<T,C> { List<T> load(); } //Converter return a list of objects of type T public interface Converter<T> { List<T> convert(Iterable<CSVRecord> csvRecords); }
So with the above interfaces, i tried implementing my classes but clearly I am going wrong, so my understanding isnt great and I would like some help as to where I am going wrong.
public class TransactionConverter<T> { public List<T> convert(Iterable<CSVRecord> records) { List<T> transactions = new ArrayList<>(); for(CSVRecord r: records){ T t = convert(r); transactions.add(t); } return transactions; } private T convert(CSVRecord r){ //TradeDate,Symbol,Buy/Sell,Quantity,TradePrice DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); LocalDate transactionDate = LocalDate.parse(r.get(0), formatter); String ticker = r.get(1); TransactionType transactionType = TransactionType.valueOf(r.get(2)); Double amount = Double.parseDouble(r.get(3)); Double quantity = Double.parseDouble(r.get(4)); //getting ERROR HERE return new Transaction(ticker, "",transactionDate, transactionType, amount, quantity); } } public class CSVFileLoader<T,C> implements FileLoader<T,C> { private String filePath; private TransactionConverter converter; private Logger logger = LoggerFactory.getLogger(CSVFileLoader.class); public CSVFileLoader(String filePath, TransactionConverter converter){ this.filePath = filePath; this.converter = converter; } @Override public List<T> load() { List<T> transactions = null; Reader in = null; Iterable<CSVRecord> records; try { in = new FileReader(filePath); records = CSVFormat.RFC4180.withHeader("ID", "CustomerNo", "Name").parse(in); transactions =converter.convert(records); } catch (FileNotFoundException e) { logger.info("Unable to load file " + filePath); } catch (IOException e) { e.printStackTrace(); } return transactions; } }
Advertisement
Answer
The source code in the question for TransactionConverter
does not implement the interface Converter
. Because it’s trying to implement it with the concrete type of Transaction
, it should specify that in the implements
clause and should not declare a type parameter for itself. It should use the concrete type of Transaction
anywhere that T
was used in Converter
. Here’s the resulting source:
public class TransactionConverter implements Converter<Transaction> { @Override public List<Transaction> convert(Iterable<CSVRecord> records) { List<Transaction> transactions = new ArrayList<>(); for(CSVRecord r: records){ Transaction t = convert(r); transactions.add(t); } return transactions; } private Transaction convert(CSVRecord r){ //TradeDate,Symbol,Buy/Sell,Quantity,TradePrice DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); LocalDate transactionDate = LocalDate.parse(r.get(0), formatter); String ticker = r.get(1); TransactionType transactionType = TransactionType.valueOf(r.get(2)); Double amount = Double.parseDouble(r.get(3)); Double quantity = Double.parseDouble(r.get(4)); //getting ERROR HERE return new Transaction(ticker, "",transactionDate, transactionType, amount, quantity); } }
The type parameter C
is not used in the FileLoader
interface, so it is redundant. The fact that a converter is used is an implementation detail of CSVFileLoader
in this code. Here’s the updated interface declaration:
public interface FileLoader<T> { List<T> load(); }
If I understand your intent, CSVFileLoader
should be able to work with any implementation of Converter
, but it currently requires a TransactionConverter
. It also uses the name transactions
for the list of results, but these might not be Transaction
objects if another type of Converter
is used. Here’s the updated implementation:
public class CSVFileLoader<T> implements FileLoader<T> { private String filePath; private Converter<T> converter; private Logger logger = LoggerFactory.getLogger(CSVFileLoader.class); public CSVFileLoader(String filePath, Converter<T> converter) { this.filePath = filePath; this.converter = converter; } @Override public List<T> load() { List<T> convertedRecords = null; Reader in = null; Iterable<CSVRecord> records; try { in = new FileReader(filePath); records = CSVFormat.RFC4180.withHeader("ID", "CustomerNo", "Name").parse(in); convertedRecords = converter.convert(records); } catch (FileNotFoundException e) { logger.info("Unable to load file " + filePath); } catch (IOException e) { e.printStackTrace(); } return convertedRecords; } }