Шаблон проектирования RAII в Java
Исходя из фона С++, я являюсь огромным поклонником шаблона RAII. Я использовал его для обработки управления памятью и управления блокировкой вместе с другими вариантами использования.
С Java 1.7 я вижу, что я могу использовать шаблон try-with-resources для создания шаблона RAII.
Я создал образец приложения, используя RAII, и он работает, но я вижу предупреждения компилятора из java.
Пример приложения
try(MyResource myVar = new MyResource(..))
{
//I am not using myVar here
}
Я получаю следующие ошибки:
warning: [try] auto-closeable resource node is never referenced in body of corresponding try statement
Я понимаю предупреждение, это подразумевает, что я должен был использовать переменную внутри блока try, которую мне действительно не нужно делать все время.
Глядя на это, я предполагаю, что Java действительно не имеет истинной поддержки для RAII, и я, возможно, неправильно использовал функцию, которая была предназначена только для управления ресурсами, а не как эквивалент RAII на С++.
Пара вопросов:
- Правильно ли я понимаю?
- Насколько рискованно игнорировать эти предупреждения?
- Как игнорировать эти предупреждения через ant?
- Есть ли простой способ преодолеть это?
для 4 я думаю о том, чтобы разбить вызов конструктора на более простой конструктор и метод экземпляра, подобный этому
try(MyResource myVar = new Resource())
{
myvar.Initialize()
....
}
Что решает проблемы с компилятором, но берет сущность из дизайна, подобного RAII.
Ответы
Ответ 1
1. Правильно ли я понимаю?
Более или менее. Да, вы можете использовать try-with-resources таким образом и да, это семантически сопоставимо с RAII. Разница заключается в отсутствии уничтожения или освобождения, только вызов метода.
Необычно найти объекты, написанные только для переноса некоторой логики управления ресурсами, например:
import java.util.concurrent.locks.Lock;
public class Guard implements AutoCloseable {
private final Lock lock;
public Guard(Lock lock) {
this.lock = lock;
lock.lock();
}
@Override
public void close() {
lock.unlock();
}
}
try(Guard g = new Guard(myLock)) {
// do stuff
}
Если вы работаете с другими программистами, возможно, вам придется объяснить, что это значит для нескольких человек, но я лично не вижу проблемы с ним, если он плавает на вашей лодке.
То, что я бы не рекомендовал, это писать странный код, например
try(AutoCloseable a = () -> lock.unlock()) {
lock.lock();
// do stuff
}
который обязательно сгенерирует WTF в обзоре кода.
2. Насколько рискованно игнорировать эти предупреждения?
Не рискованно. Предупреждение - это просто уведомление. Знаете, если вы не знали об этом.
Чтобы избавиться от предупреждения, вы можете попробовать:
try(@SuppressWarnings("unused")
MyResource myVar = new MyResource())
Или, может быть, посмотрите 'Как вы получаете * ant *, чтобы не печатать предупреждения javac?.
IDE должна предоставить вам возможность подавить конкретное предупреждение либо глобально, либо только для одного оператора (без аннотации).
Ответ 2
Расширить ответ Radiodef. Я думаю, что RAII с try-with-ресурсами является полностью приемлемым шаблоном для java. Но чтобы действительно подавить предупреждение
- вам нужно использовать
@SuppressWarnings("try")
вместо @SuppressWarnings("unused")
.
- И добавьте аннотацию к методу вместо объявления переменной
Пример с приведенными выше пунктами:
@SuppressWarnings("try")
void myMethod1() {
try(MyResource myVar = new MyResource(..)) {
//I am not using myVar here
}
}
Развертывание по самому шаблону. Я широко использовал его для управления блокировкой чтения и записи, и он отлично работал.
В моем коде я использовал трюк, чтобы когда-нибудь превентивно разблокировать некоторый ресурс, чтобы увеличить concurrency, например:
try (Guard g1 = new Guard(myLock1)) {
someStuffThatRequiresOnlyLock1();
try (Guard g2 = new Guard(myLock2)) {
someStuffThatRequiresBothLocks();
if (isSomething) {
g1.close();
someMoreSuffThatRequiresOnlyLock2()
} else {
someMoreSuffThatRequiresBothLocks();
}
}
}
Замки всегда приобретаются в том же порядке, но разблокировка выполняется по мере необходимости, оставляя как можно больше места для параллельной обработки. Настройка, чтобы заставить ее работать с блокировками чтения и записи, заключается в изменении класса Guard для повторного закрытия:
public class Guard implements AutoCloseable {
private final Lock lock;
private boolean isClosed = false;
public Guard(Lock lock) {
this.lock = lock;
lock.lock();
}
@Override
public void close() {
if (!isClosed) {
isClosed = true;
lock.unlock();
}
}
}