#java #exception-handling #recursion
#java #исключение #рекурсия
Вопрос:
Я пишу программу чтения файлов, которая возвращает объект, и я бы хотел, чтобы она предупреждала об ошибках синтаксического анализа и переходила к следующей записи.
Приведенный ниже код является очевидной реализацией этого, но включает рекурсию изнутри блока catch. Есть ли какая-либо техническая или стилистическая причина не делать этого?
public RecordType nextRecord() throws IOException{
if (reader == null){
throw new IllegalStateException("Reader closed.");
}
String line = reader.readLine();
if (line == null){
return null;
}else{
try {
return parseRecord(line);
}catch (ParseException pex){
logger.warn("Record ignored due to parse error: "
pex.getMessage());
//Note the recursion here
return nextRecord();
}
}
}
Комментарии:
1. Проблема в том, что ваша программа будет накапливать пространство стека с каждой ошибочной записью, что делает ее уязвимой. Например, когда этот фрагмент кода будет частью веб-службы, злоумышленник может передавать только много-много записей об ошибках, пока вся служба не умрет из-за переполнения стека. Это была бы успешная атака типа «отказ в обслуживании».
2. @Ingo спасибо, это должен быть ответ :-).
Ответ №1:
Я бы предпочел использовать цикл. С рекурсией вы никогда не знаете, насколько глубоко вы можете безопасно зайти.
String line;
while((line = reader.readLine()) != null) {
try {
return parseRecord(line);
}catch (ParseException pex){
logger.warn("Record ignored due to parse error: " pex);
}
}
return null;
Комментарии:
1. Да, и отладка этого StackOverflowError была бы затруднительной, не так ли?
2. По крайней мере, перед ошибкой вы бы увидели множество предупреждений. 😉
Ответ №2:
Почему бы не заменить рекурсию циклом:
public RecordType nextRecord() throws IOException {
if (reader == null) {
throw new IllegalStateException("Reader closed.");
}
for (;;) {
String line = reader.readLine();
if (line == null) {
return null;
} else {
try {
return parseRecord(line);
} catch (ParseException pex) {
logger.warn("Record ignored due to parse error: "
pex.getMessage());
// continue to the next record
}
}
}
}
Стилистически я нахожу это предпочтительным.
Комментарии:
1. Это немного менее понятно (спорно), и вы не ответили на вопрос: есть ли какая-либо причина не использовать рекурсию.
2. @C. Ross: Последнее предложение — мой (субъективный) аргумент в пользу того, почему я считаю это предпочтительным.
Ответ №3:
Было бы чище позволить исключению ParseException распространяться обратно на вызывающий объект? Затем вызывающий может решить, что с этим делать.
Ответ №4:
Мне кажется, что независимо от того, что вызывает ваш метод, он будет продолжать вызывать его до тех пор, пока метод не вернет null.
Я бы, вероятно, последовал совету предыдущих постеров и использовал цикл, однако я бы посмотрел на то, что вызывает метод (поскольку он, вероятно, уже использует цикл), пропустил строку, ища исключение, которое будет сгенерировано.