Учитывая класс MyClass
, использующий внутренний закрываемый объект, myCloseable
и предоставляющий метод getCloseable()
, который его возвращает; Eclipse, если он настроен для такого рода предупреждений о закрываемых ресурсах, будет систематически предупреждать каждый раз, когда кто-либо вызывает getCloseable()
, говоря, что закрываемый объект «не может быть закрыт в этом месте».
Приятно иметь такие предупреждения о ресурсах, которые следует закрыть, если кто-то забудет их закрыть, поэтому я предпочитаю оставлять эту проверку включенной. Но в только что описанном сценарии это довольно раздражает. В принципе представляется возможным пометить метод getCloseable()
аннотацией, скажем, @existingCloseable
, сообщая компилятору, что «все в порядке, я просто возвращаю уже существующий ресурс, а не создаю новый, поэтому вызывающая сторона не должна закрой его".
Рассматривалась ли такая аннотация для принятия Eclipse или другими IDE? Обсуждений на эту тему я не нашел, так что подозреваю, что нет. Интересно, почему: закономерность, описанная в моем примере, кажется мне вполне обычной и естественной. Будет ли решение, которое я рассматриваю с аннотациями, не работать или иметь недостатки, о которых я не подумал?
Пример
Вот (глупый) пример, где закрывающимся является OutputStream
. Метод fromPath
выдает предупреждение (если не подавлен) о том, что s
не закрыт, и я не обращаю на это внимания: это предупреждение кажется адекватным, и его нужно подавить только один раз. Раздражающая часть и объект моего вопроса — это предупреждение, которое появляется из метода main
: «Потенциальная утечка ресурсов: «цель» не может быть закрыта». Это предупреждение будет появляться каждый раз, когда любой пользователь моего класса будет использовать getTarget
. Я хотел бы отключить его раз и навсегда, аннотировав метод getTarget
, чтобы компилятор знал, что этот метод возвращает ресурс, который вызывающая сторона не должна закрывать. Насколько мне известно, в настоящее время это не поддерживается в Eclipse, и мне интересно, почему.
public class MyWriter implements AutoCloseable {
public static void main(String[] args) throws Exception {
try (MyWriter w = MyWriter.fromPath(Path.of("out.txt"))) {
// …
OutputStream target = w.getTarget();
target.flush();
// …
}
}
@SuppressWarnings("resource")
public static MyWriter fromPath(Path target) throws IOException {
OutputStream s = Files.newOutputStream(target);
return new MyWriter(s);
}
private OutputStream target;
public OutputStream getTarget() {
return target;
}
private MyWriter(OutputStream target) {
this.target = target;
}
@Override
public void close() throws Exception {
target.close();
}
}
Обсуждение
Я отредактировал свой вопрос, в котором изначально спрашивалось, можно ли изменить мой код, чтобы избежать предупреждения: я понял, что на самом деле это не тот вопрос, который меня интересует, меня скорее интересует решение на основе аннотаций, о котором я думал - извините за это.
Я понимаю, что этот пример глупый: без дальнейшего контекста, и, как правильно указывают несколько ответов, похоже, что я должен скорее обернуть все методы потока и никогда не возвращать поток во внешний мир. К сожалению, трудно сделать этот пример реалистичным, при этом сохранив его размер.
Однако один пример хорошо известен, поэтому мне не нужно приводить его здесь в подробном виде: в сервлете можно вызвать getOutputStream()
, и это типичный случай, иллюстрирующий мою точку зрения: метод getOutputStream()
возвращает поток, который вызывающей стороне не нужно закрывать , но Eclipse будет предупреждать о потенциальной утечке ресурсов при каждом вызове (то есть в каждом сервлете), что доставляет неудобства. Также понятно, почему концепты этого метода просто не обернули все вокруг вместо того, чтобы вернуть сам поток: полезно получить объект, реализующий этот всем известный интерфейс, ведь его потом можно будет использовать с другими библиотеками и методами которые ожидают взаимодействия с потоком.
В качестве еще одной иллюстрации моей точки зрения представьте, что метод getCloseable()
используется только внутри, поэтому метод виден пакету, а не общедоступен. Реализация getCloseable()
может быть сложной, например, с наследованием, так что не всегда возможно заменить этот вызов простым доступом к базовому полю, как в моем небольшом примере. В таком случае кажется, что не KISS вообще реализует целую оболочку вместо возврата уже существующего интерфейса, просто чтобы компилятор был доволен.
toPath
должен возвращатьPath
, чтобы соответствовать соглашению, поэтому имя неверно. Вероятно, это должно бытьofPath(...)
. - person Jim Garrison   schedule 27.12.2020OutputStream
только для того, чтобы открыть обернутый объект? Без дополнительного контекста неясно, чего вы пытаетесь достичь. Почему клиентуMyWriter
нужен необработанный доступ к базовомуOutputStream
? Не можетMyWriter
иметь методы, которые проксируют необходимую функциональность, не раскрывая файлOutputStream
. Извините, но этот код выглядит забавно, и я подозреваю, что здесь скрывается проблема XY. - person Jim Garrison   schedule 27.12.2020toPath
- неподходящее имя; Я забыл переименовать метод после того, как как-то упростил свой пример. Я также отредактировал сообщение, чтобы прояснить свой вопрос и лучше описать некоторые примеры сценариев, в которых может возникнуть проблема. - person Olivier Cailloux   schedule 27.12.2020