I would always use enum here, since enum is inherently ordered, but Map is not.
Using enum , you can generate a CTL file from the enumeration itself and use enum values ββas factories to populate your csv file.
class MyObj { final String foreName; final String surname; public MyObj(String foreName, String surname) { this.foreName = foreName; this.surname = surname; } public String getForeName() { return foreName; } public String getSurname() { return surname; } } enum Column { Forename { @Override String fromMyObj(MyObj it) { return it.getForeName(); } }, Surname { @Override String fromMyObj(MyObj it) { return it.getSurname(); } },; abstract String fromMyObj(MyObj it); static String asSelectStatement(Set<Column> columns, String tableName) { return join(columns, ",", "SELECT ", " FROM " + tableName); } static String asCSVHeader(Set<Column> columns) { return join(columns, ","); } static String asCSV(Set<Column> columns, MyObj it) { return join(columns, (Column a) -> a.fromMyObj(it), ","); } private static String join(Set<Column> columns, String between) { return join(columns, new StringJoiner(between)); } private static String join(Set<Column> columns, String between, String prefix, String suffix) { return join(columns, new StringJoiner(between, prefix, suffix)); } private static String join(Set<Column> columns, StringJoiner joined) { return join(columns, (Column a) -> a.name(), joined); } private static String join(Set<Column> columns, Function<Column, String> as, String between) { return join(columns, as, new StringJoiner(between)); } private static String join(Set<Column> columns, Function<Column, String> as, String between, String prefix, String suffix) { return join(columns, as, new StringJoiner(between, prefix, suffix)); } private static String join(Set<Column> columns, Function<Column, String> as, StringJoiner joined) { for (Column c : columns) { joined.add(as.apply(c)); } return joined.toString(); }