Шаблон проектирования 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();
        }
    }
}