Здравствуйте. Подскажите, как написать обобщённый метод чтения данных из файла. Например, в одном случае нужно считать строки, в другом - целые числа. Как написать метод типа такого:
public static <T> List<T> readFromFile(Path pathIn, TypeValue typeValue){...};
Корректен ли такой код:
public static List readFromFile(Path pathIn, TypeValue typeValue){
try(Scanner in = new Scanner(pathIn)) {
if (typeValue == TypeValue.typeInt) {
List<Integer> list = new ArrayList<>();
while (in.hasNextInt()) {
list.add(in.nextInt());
}
return list;
} else {
List<String> list = Files.readAllLines(pathIn);
return list;
}
} catch (IOException e){
return null;
}
}
Здесь pathIn
- путь к файлу с данными, typeValue
- флаг для обозначения данных (Integer
, String
и т.д.).
Смущает, что возвращается не параметризованный List
. Как вообще решается задача чтения из файла, если формат данных может быть разным - Integer
, Double
, String
и т.д.? Или следует писать для каждого типа данных свой код для чтения? Как-то громоздко тогда получается.
Функционал из Java 8 Stream & Lambda позволяет передавать в метод функциональный интерфейс, который будет применен к потоку данных:
public void start() throws IOException {
Path path = Paths.get("test");
List<String> resultStringList = readFile(path, Function.identity());
List<Integer> resultIntList = readFile(path, Integer::parseInt);
}
public <T> List<T> readFile(Path path, Function<String, T> func) throws IOException {
return Files.lines(path)
.map(func)
.collect(Collectors.toList());
}
}
Или следует писать для каждого типа данных свой код для чтения?
Не самый плохой вариант: написать отдельные методы для каждого типа (в Scanner
, например, так и сделали) .
Как-то громоздко тогда получается.
В Вашем варианте вместо нескольких коротких методов используется один, в котором в одном большом if
будет код всех этих методов.
Альтернатива
Можно обернуть метод в интерфейс и применить полиморфизм:
//интерфейс
interface ListReader<T> {
List<T> read(String path);
}
//все общие функции вынесем в отдельный базовый класс
abstract class BaseListReader<T> implements ListReader<T> {
@Override
public List<T> read(String path) {
try (Scanner in = new Scanner(path)) {
List<T> list = new ArrayList<>();
while (hasNext(in)) {
list.add(next(in));
}
return list;
} catch (IOException e) {
return null;
}
}
abstract boolean hasNext(Scanner in) throws IOException;
abstract T next(Scanner in) throws IOException;
}
class IntListReader extends BaseListReader<Integer> {
@Override
boolean hasNext(Scanner in) throws IOException {
return in.hasNextInt();
}
@Override
Integer next(Scanner in) throws IOException {
return in.nextInt();
}
}
class StringListReader extends BaseListReader<String> {
@Override
boolean hasNext(Scanner in) throws IOException {
return in.hasNext();
}
@Override
String next(Scanner in) throws IOException {
return in.nextLine();
}
}
Затем создать фабрику:
//Метод для создания
public ListReader createReader(Class clazz) {
if (clazz == Integer.class) {
return new IntListReader();
} else if (clazz == String.class) {
return new StringListReader();
}
throw new IllegalArgumentException("clazz");
}
В такой реализации обработка разных типов будет отделена друг от друга и от классов-клиентов (публичным может быть только интерфейс ListReader
). Это позволит протеститровать их отдельно от других классов приложения/использовать класы повторно.
Насколько это оправдано нужно решать по ситуации. Если код будет использован один раз, требования к нему изменяться не будут и нет строгих требований к качеству (покрытие тестами, ограничение сложности методов), то может быть достаточно просто разбить на отдельные методы.
Смущает, что возвращается не параметризованный List
Эта проблема не решена (возвращается не параметризованный ListReader
). Решение зависит от того как используется результат метода. Если полученный список обрабатывается как List<Object>
то метод можно оставить непараметризованным. Если в дальнейшем объекты из списка приводятся к типу, то стоить рассмотреть возможность параметризации фабрик и клиентского кода.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Есть БДПосле создания появляется необходимость в добавлении таблиц но для этого надо вызвать метод onUpgrade и когда вызовается этот метод просто...
Решил подключить к своему приложению базу данных PostgreSQLдобавил в настройках идеи нужный jar архив, для проверки написал такую строку и запустил: