Search code examples
javagenericsrefactoring

Refactor multiple methods into a generic one


I would like to refactor my code. I need to create a generic method to avoid duplicate code.

I have created a utility class to read CSV files using jackson-dataformat-csv.

public class CsvUtils<T> {

    private final Class<T> typeParameterClass;

    public CsvUtils(final Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }
    
    private static final char DEFAULT_SEPARATOR = ';';

    private static final String CSV_EXTENSION = ".csv";

    private static final String UNDERSCORE = "_";

    private static final String SOURCE_PATH = "./src/test/resources/";

    public List<T> readCSV(final String fileName) throws IOException {
        return readCSV(fileName, DEFAULT_SEPARATOR);
    }
    
    public List<T> readCSV(final String fileName, final char separator) throws IOException {
        final Reader reader = new FileReader(SOURCE_PATH + fileName + CSV_EXTENSION);

        final CsvMapper mapper = new CsvMapper();
        final CsvSchema schema = CsvSchema.emptySchema().withColumnSeparator(separator).withHeader();

        final MappingIterator<T> mappingIterator = mapper
            .readerFor(typeParameterClass)
            .with(schema)
            .readValues(reader);

        return mappingIterator.readAll();
    }

}

and I use the readCSV method in the same way for each entity classes:

private List<Audi> getAudi() throws IOException {
    CsvUtils<Audi> csvAudi = new CsvUtils<>(Audi.class);
    return csvAudi.readCSV("Audi");
}

private List<BMW> getBMW() throws IOException {
    CsvUtils<BMW> csvBMW = new CsvUtils<>(BMW.class);
    return csvBMW.readCSV("BMW");
}

private List<Mercedes> getMercedes() throws IOException {
    CsvUtils<Mercedes> csvMercedes = new CsvUtils<>(Mercedes.class);
    return csvMercedes.readCSV("Mercedes");
}

The generics method will be:

private static <T extends Car> List<T> getT() throws IOException {
   
    Class<?> typeClass = ???;       

    CsvUtils<T> csvT = new CsvUtils<>(typeClass);
    return csvT.readCSV(typeClass.getSimpleName());

}

but I don't know how to extract the class from a generic type.

Any documentation will be appreciated! Thanks.


Solution

  • The type parameter exists for the compiler to enforce type safety and it is replaced by either its lower bound or Object if it is unbounded during compilation. At runtime you don't know whether getT was invoked to assign its result to a list of Car, BMW or Mercedes.

    That's why you need to pass a parameter of type Class<T> if you want to access the actual type of type parameter T at runtime.

    More information on type erasure can be found e. g. in the JLS or the Java tutorial.