Если вы хотите использовать какой-либо объект AutoClosable, вы должны использовать попытка с ресурсами. Ok. Но что, если я хочу написать метод, который возвращает AutoClosable? После того, как вы создали или откуда-то получили объект AutoCloseable, вы должны закрыть его в случае исключения, например так:
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some AutoClosable
try {
... // some work
}
catch (Throwable exception) {
autoCloseable.close();
throw exception;
}
return autoCloseable;
}
Если вы не напишите блок try/catch, вы утечете ресурс, который содержит этот объект autoCloseable, в случае исключения в строке // some work.
Но этого try/catch недостаточно, потому что autoCloseable.close() тоже может генерировать исключение (по задумке). Таким образом, приведенный выше код преобразуется в
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some autoclosable
try {
... // some work
}
catch (Throwable exception) {
try {
autoCloseable.close();
}
catch (Throwable exceptionInClose) {
exception.addSuppressed(exceptionInClose);
throw exception;
}
throw exception;
}
return autoCloseable;
}
Это много шаблонов. Есть ли лучший способ сделать это в java?
@Kiskae, почему он закрывается в операторе return в приведенном выше примере кода?




Есть ряд приближенных.
AutoCloseable и поместите его в свою попытку с ресурсом.Редактировать: Я решил вернуться к ответу, добавив несколько примеров кода для развлечения.
Идиомы Execute Around
Простое и лучшее решение. К сожалению, в библиотеке Java он используется нечасто (AccessController.doPrivileged — большое исключение), и соглашения не установлены. Как всегда, проверенные исключения Java без поддержки функций усложняют ситуацию. Мы не можем использовать java.util.function и должны изобретать собственные функциональные интерфейсы.
// Like Consumer, but with an exception.
interface Use<R, EXC extends Exception> {
void use(R resource) throws EXC;
}
public static void withThing(String name, Use<InputStream,IOException> use) throws IOException {
try (InputStream in = new FileInputStream(name)) {
use.use(in);
}
}
Красиво и просто. Не нужно беспокоиться о том, что клиентский код испортит обработку ресурсов, поскольку он этого не делает. Хороший.
Модифицированная попытка с ресурсом как библиотечная функция, реализованная как прокси AutoCloseable в попытке с ресурсом
Это будет некрасиво. Нам нужно передать получение, выпуск и инициализацию как лямбда-выражения. Создание ресурса непосредственно в этом методе открывает небольшое окно, в котором неожиданное исключение может привести к утечке.
public static InputStream newThing(String name) throws IOException {
return returnResource(
() -> new FileInputStream(name),
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
Общая реализация returnResource будет выглядеть так, как показано ниже. Хак, потому что попытка с ресурсом не поддерживает такие вещи, а библиотека Java плохо поддерживает проверенные исключения. Примечание ограничено одним исключением (вы можете использовать непроверенное исключение для непроверенных исключений).
interface Acquire<R, EXC extends Exception> {
R acquire() throws EXC;
}
// Effectively the same as Use, but different.
interface Release<R, EXC extends Exception> {
void release(R resource) throws EXC;
}
public static <R, EXC extends Exception> R returnResource(
Acquire<R, EXC> acquire, Release<R, EXC> release, Use<R, EXC> initialize
) throws EXC {
try (var adapter = new AutoCloseable() { // anonymous classes still define type
private R resource = acquire.acquire();
R get() {
return resource;
}
void success() {
resource = null;;
}
public void close() throws EXC {
if (resource != null) {
release.release(resource);
}
}
}) {
R resource = adapter.get();
initialize.use(resource);
adapter.success();
return resource;
}
}
Возможно, это будет чище, если мы отделим аргумент, с помощью которого мы конструируем ресурс, от конструирования ресурса.
public static InputStream newThing(String name) throws IOException {
return returnResource(
name,
FileInputStream::new,
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
// Like Function, but with a more descriptive name for a functional interface.
interface AcquireFrom<T, R, EXC extends Exception> {
R acquire(T t) throws EXC;
}
public static <T, R, EXC extends Exception> R returnResource(
T t, AcquireFrom<T, R, EXC> acquire, Release<R, EXC> release, Use<R, EXC> initialize
) throws EXC {
return returnResource(() -> acquire.acquire(t), release, initialize);
}
Итак, в целом, следующие вещи являются болью:
java.util.function не поддерживает отмеченные исключения.AutoCloseable::close объявляя, что он выдает Exception вместо того, чтобы быть параметром типа типа.
Вопрос будет заключаться в том, почему вы хотите вернуть
autoclosable?, поскольку он уже был бы закрыт в операторе return.