Как изменить список за пределами потребителя

Я работаю над проблемой для школы. Многие из этих методов уже были реализованы, и мне не разрешено вносить слишком много изменений.

На самом деле я могу вносить изменения только в определенных местах.

Вот код метода, над которым я работаю, хотя некоторые слова на нидерландском языке, он должен быть читаемым.

Он должен прочитать строки файла, создать адреса из текста (который сохраняется как (улица +" "+ номер +" "+ место)) и добавить их в список, который возвращается. Файл заканчивается пустой строкой.

@Override
public List<Adres> query(ISpecification specification) {
    if (specification instanceof FileSpecification) {
        if (((FileSpecification) specification).toFileQuery().equals("ALL")) {
            ArrayList<Adres> adressen = new ArrayList<>();
/*---start of my code*/
            File studentF = new File(fsConnection.getStudentConnection());
            try {
                FileReader fr = new FileReader(studentF);
                BufferedReader br = new BufferedReader(fr);
                br.lines().forEach(new Consumer<String>(){
                    @Override
                    public void accept(String line) {
                        String[] words = line.split("\\s");
                        if(words.length == 3){
 /*line i'm having trouble with*/adressen.add(new Adres(words[0], Integer.parseInt(words[1]), words[2]);
                        }
                    } 
                });
            } catch (FileNotFoundException ex) {
                Logger.getLogger(AdresFile.class.getName()).log(Level.SEVERE, null, ex);//Don't mind this
            }
/*---end of my code*/
            //System.out.println("query: Nog niet geimplementeerd!");
            return adressen;
        } else {
            return null;
        }
    } else {
        return null;
    }
}

Как видите, я хотел получить доступ к списку за пределами потребительского блока. Теперь я знаю, что это невозможно. Я думал о создании другого метода или около того, но это не разрешено. Я должен использовать метод foreach. Любая помощь приветствуется.

3 ответа

Когда вы работаете с Java7, компилятору потребуется

final ArrayList<Adres> adressen = new ArrayList<>();

там. Дело в том, что вы хотите использовать эту локальную переменную в аномальном внутреннем классе; другими словами: в контексте, который каким-то образом отделен от класса, вы помещаете исходный код. И для того, чтобы разъединенный класс мог использовать adressen, он должен быть окончательным (чтобы компилятор знал: эта ссылка не изменится позже). И учитывая ваш комментарий: нет, это не волшебным образом превращает объект во что-то неизменное. Это просто предотвращает изменение ссылки на "цель", на которую она указывает!

Но так как вы не можете изменить эту строку, вы можете пойти на:

ArrayList<Adres> adressen = new ArrayList<>();
final ArrayList<Adres> tempAdressen = adressen;

а затем ваш код использовать tempAdressen.

В качестве альтернативы, я предполагаю, что вы используете Java7. С Java8 компилятор должен понимать, что adressen фактически является окончательным; и, таким образом, он должен принять исходный код как есть.

Кажется, вы уже используете Java8 из-за вызова lines() в BufferedReader.

Поэтому я предлагаю сделать карту, а затем собирать в список вместо forEach. Таким образом, вам не нужно будет получать доступ к списку из вашего потребителя.

adressen.addAll(
    br.lines().map(line -> {
                    String[] words = line.split("\\s");
                    if (words.length == 3) {
                        return new Adres(words[0], Integer.parseInt(words[1]), words[2]);
                    }
                    return null;
                })
          .filter(Objects::nonNull)
          .collect(Collectors.toList())
);

В Java8 вы можете использовать java.util.stream.Collectors для возврата списка элементов непосредственно из потока. Использование коллекторов поможет вам избежать побочных эффектов (в вашем случае необходимо использовать внешний массив для разбора элементов).

Я лично написал бы это, используя следующую лямбду:

List<Adres> adressen = br.lines().stream()
    .map(line -> line.split("\\s"))
    .filter(words -> words.length == 3)
    .map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
    .collect(Collectors.toList());

Это будет работать, но не будет обрабатывать случай, когда данные искажены. Чтобы решить эту проблему, можно изменить приведенный выше код для обработки, если строка не состоит из 3 элементов, изменив лямбду (даже если это не очень элегантно):

List<Adres> adressen = br.lines().stream()
    .map(line -> line.split("\\s"))
    .filter(words -> {
         if (words.length == 3)
             return true;
         else {
             throw new IllegalArgumentException();
         }
    })
    .map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
    .collect(Collectors.toList());
Другие вопросы по тегам