Ловить ошибки при создании объекта из try-with-resources отдельно от тела
Резюме
У меня есть закрытый тип, CloseableClass
который может бросить IOError в своем конструкторе, методах и, возможно, даже внутри close
, Я хочу использовать try-with-resources и по-прежнему иметь дело с ошибками во время построения, в отличие от ошибок во время использования (использование включает в себя очистку). Еще лучше, я хотел бы написать поддерживаемый код.
Допустим, вы хотите создать закрываемый экземпляр класса и использовать его с оператором try-with-resources. Это может бросить IOException
как в его конструкторе, так и в методе, используемом в теле try-with-resources:
import java.io.Closeable;
import java.io.IOException;
import java.util.Random;
public class CloseableClass implements Closeable {
public CloseableClass() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public void internetStuff() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public void close() throws IOException {
if (new Random().nextBoolean()) {
throw new IOException();
}
}
public static void main(String[] args) {
try (CloseableClass closeable = new CloseableClass()) {
closeable.internetStuff();
}
catch (IOException e) {
System.out.println("Bad error!");
}
}
}
Допустим, вы хотите разобраться с ошибками, сгенерированными в конструкторе и теле отдельно. Есть ли поддерживаемый способ сделать это? В Python я бы сделал:
try:
closeable = CloseableClass()
except IOException:
print("Constructor error")
return
try:
with closeable:
closeable.internet_stuff()
except IOException:
print("Body error")
но в Java вы не можете без назначения второго имени объекту:
CloseableClass closeable_;
try {
closeable_ = new CloseableClass();
}
catch (IOException e) {
System.out.println("Constructor error!");
return;
}
try (CloseableClass closeable = closeable_) {
closeable.internetStuff();
}
catch (IOException e) {
System.out.println("Body error!");
}
Мне сказали, что это "не поддерживаемый код" в первую очередь из-за использования closeable_
и я не далеко согласен. Я хочу избегать использования try-finally, потому что тогда у вас есть еще более серьезная проблема с его эмуляцией:
CloseableClass closeable;
try {
closeable = new CloseableClass();
}
catch (IOException e) {
System.out.println("Constructor error!");
return;
}
try {
closeable.internetStuff();
}
catch (IOException e) {
try {
closeable.close();
}
catch (IOException ignore) {
// Already dealing with this
}
System.out.println("Body error!");
}
finally {
try {
closeable.close();
}
catch (IOException e) {
System.out.println("Body error!");
}
}
Обратите внимание, что это требует второго вызова close
быть неоператором, которого не соблюдает тестовый класс (обратите внимание, что AutoCloseable
не требует этого, хотя Closeable
делает). Это немного лучше, когда close
не могу бросить, но не сильно.
В основном проблема в том, что
close
может бросить- Закрыть, прежде чем иметь дело с
IOException
предотвратить печать"Body error!"
дважды - Не очевидно, как заставить его работать с несколькими инициализаторами из try-with-resources
- Вы в конечном итоге дублируете код в любом случае.
Я просто вынужден жить с "не поддерживаемым кодом" или я упускаю из виду хороший способ справиться с этим?
3 ответа
"Обратите внимание, что для закрытия не требуется второй вызов", - "Нет, вам не нужно" close()
в catch
блок, как finally
Блок всегда будет выполняться. Вы будете использовать только close()
внутри catch
заблокировать, если вы завершите JVM с помощью вызова как System.exit()
в catch
блок. Вообщем выкинешь Exception
звонить из catch
часы, но вы будете выполнять уборку в finally
блокировать большую часть времени.
Попробуйте с ресурсом лучше, но вы можете использовать тип и описание Exception
брошен, чтобы расшифровать, что и где пошло не так.
РЕДАКТИРОВАТЬ
Насколько мне известно, я предлагаю:
1) Попробуйте с ресурсом:
try(Resource resource = new Resource()){
// use resource.
}catch(Exception e){
// handle exception.
// OR better to throw exception to caller.
throw e;
}
2) Обычный стиль:
Resource resource = null;
try{
resource = new Resource();
// use resource
}catch(Exception e){
// handle exception.
// OR better to throw exception to caller.
throw e;
} finally {
if(resource != null){
try{
resource.close();
} catch(Exception e){
// most of time you wont or cant do anything here.
}
}
}
Одно из решений - определить метод, который объединяет ошибки инициализации в настраиваемый тип исключения, а затем использовать его для определения момента возникновения ошибок.
private CloseableClass createCloseable() throws CloseableCreateException{
try {
return new CloseableClass();
} except (IOException e) {
throw new CloseableCreateException(e);
}
}
try (CloseableClass closeable = initCloseable()) {
closeable.internetStuff();
} catch (CloseableCreateException e) {
System.out.println("Constructor error!");
} catch (IOException e) {
System.out.println("Body error!");
}
Еще одно простое, но несколько неэлегантное решение - использовать логический флаг:
boolean init = true;
try (CloseableClass closeable = new CloseableClass()) {
init = false;
closeable.internetStuff();
} catch (IOException e) {
if (init) {
System.out.println("Constructor error!");
} else {
System.out.println("Body error!");
}
}
Начиная с Java 9, функция try-with-resources принимает «фактически окончательные» переменные, поэтому вам не нужно переназначать переменную.
CloseableClass closeable;
try {
closeable = new CloseableClass();
}
catch (IOException e) {
System.out.println("Constructor error!");
return;
}
try (closeable) {
closeable.internetStuff();
}
catch (IOException e) {
System.out.println("Body error!");
}