Ловить ошибки при создании объекта из 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!");
}
Другие вопросы по тегам