Как реализовать динамическое создание объектов класса?

Есть задача cчитывать из файла построчно имя и возраст, реализовать статический метод, создающий объекты класса Human и заносящий его в List<Human>.
У меня реализован отдельный "сбор" возраста и имен, но я не знаю, как реализовать инициализацию новых объектов динамически. Ничего не понял про метод newInstanse() из той документации, которую нашел.

List<Human> people = new ArrayList<>();
List<Integer> numbers = new ArrayList<>();
List<String> names = new ArrayList<>();

try (Scanner sc = new Scanner(file)) {
    while (sc.hasNextLine()) {
        String[] nums = sc.nextLine().replaceAll("[^\\d-]+", " ").split(" ");
        for (String n : nums) {
            if (!n.isEmpty()) {
                numbers.add(Integer.valueOf(n));
            }
        }
    }
    while (sc.hasNextLine()) {
        String[] name = sc.nextLine().replaceAll("[^A-Za-zА-Яа-я]", "").split(" ");
        for (String nm : name) {
            if (!nm.isEmpty()) {
                names.add(nm);
            }
        }
    }
    if (!isPos(numbers)) {
        try {
            throw new IOException();
        }
        catch (IOException e){
            System.out.println("Некорректный входной файл");
        }
    }
    else {
        for (int i = 0; i < names.size(); i++) {
            people.add(new Human(names.get(i), numbers.get(i)));
        }
    }
}
catch (FileNotFoundException e) {
    System.out.println("Файл не найден.");
}
System.out.println(people);

Прошу оставить блоки try/catch без комментариев, они нужны по условию задачи!

При выводе списка people получаются пустые скобки []. Что не так?

Пример списка в файле

Амир 18

Тагир 18

Хамид 18

Усман 26

Нурадил 17

Ответы (1 шт):

Автор решения: Nowhere Man

Основная проблема представленного кода в том, что он не может заполнить список с именами, так как сканер на первом проходе в цикле while (sc.hasNextLine()) обрабатывает только числовые данные (возраст), и соответственно второй цикл не выполнится.

Если основная задача состоит в том, чтобы вернуть заполненный список Human, создание и раздельное заполнение двух списков numbers и names представляется излишним.

Поскольку каждая непустая строка в файле представляет собой некую целостную сущность данных, достаточно реализовать чтение имени и возраста для такой строки, и прерывать чтение при обнаружении некорректных данных.
Это также позволит сократить количество медленных операций ввода/вывода (чтения файла).

List<Human> people = new ArrayList<>();
try (Scanner sc = new Scanner(file)) {
    while (sc.hasNextLine()) {
        String line = sc.nextLine().trim();
        if (line.isEmpty()) {  // пропускаем пустую строку
            continue;
        }
        String[] parts = line.split("\\s+");
        String name = parts[0];              // имя в первой части строки
        int age = Integer.valueOf(parts[1]); // возраст во второй части
        if (age < 0) {
            throw new IllegalArgumentException("Неверный возраст в строке " + line);
        }
        people.add(new Human(name, age));
    }
}
catch (FileNotFoundException e) {
    System.out.println("Файл не найден.");
}
catch (IllegalArgumentException e) {
    System.out.println("Некорректный входной файл");
}
System.out.println(people);

Логику обработки каждой строки можно переместить в отдельный метод parseHuman, например, если допускается, что поля имени и возраста в данном файле находятся на разных позициях в строке:

Василий 25
30 Николай

Данный метод реализует следующий функционал:

  1. Разобьёт входную строку по определённому разделителю
  2. Проверит наличие ровно двух полей в строке, если окажется не два поля, будет выброшено исключение
  3. Проверит, содержит ли строка поля имени и возраста в нужном формате (проверка на отрицательное значение станет не нужна), и выбросит исключение при необходимости
  4. Вернёт экземпляр класса Human, содержащий проверенные данные.
private static final String VALID_NAME = "\\p{L}+";
private static final String VALID_AGE = "\\d{1,3}";

private static Human parseHuman(String line) {
    String[] parts = line.split("\\s+");
    if (parts.length != 2) {
        throw new IllegalArgumentException("Строка не содержит два поля: " + line);
    } 
    String name;
    int age;
    if (parts[0].matches(VALID_NAME) && parts[1].matches(VALID_AGE)) {
        name = parts[0];
        age = Integer.valueOf(parts[1]);
    } else if (parts[1].matches(VALID_NAME) && parts[0].matches(VALID_AGE)) 
        name = parts[1];
        age = Integer.valueOf(parts[0]);
    } else {
        throw new IllegalArgumentException("Некорректная строка: " + line);
    }
    return new Human(name, age);
}

Здесь регулярка "\\p{L}+" проверит, что имя является непустой последовательностью любых букв, и соответственно "\\d{1,3}" -- что возраст содержит непустую последовательность не более трёх цифр (без знака!).

Соответственно, логика кода для чтения файла сведётся к вызову указанного метода для каждой строки:

List<Human> people = new ArrayList<>();
try (Scanner sc = new Scanner(file)) {
    while (sc.hasNextLine()) {
        String line = sc.nextLine().trim();
        if (!line.isEmpty())
            people.add(parseHuman(line));
    }
}
catch (FileNotFoundException e) {
    System.out.println("Файл не найден.");
}
catch (IllegalArgumentException e) {
    System.out.println("Некорректный входной файл");
}
System.out.println(people);
→ Ссылка