RAII design pattern in Java

1. Is my understanding correct?

More or less. Yes, you can use try-with-resources this way and yes, it is semantically comparable to RAII. The difference is there is no destruction or deallocation, only a method call.

It's uncommon to find objects written just to wrap some resource management logic e.g.:

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
}

If you're working with other programmers, you might have to explain what it means to a few people but I don't personally see a problem with it if it floats your boat.

What I wouldn't recommend is writing weird code like

try(AutoCloseable a = () -> lock.unlock()) {
    lock.lock();
    // do stuff
}

which is sure to generate WTFs in code review.

2. How risky is it to ignore these warnings?

Not risky. The warning is really just a notification. You know, in case you didn't know about it.

To get rid of the warning you could try:

try(@SuppressWarnings("unused")
    MyResource myVar = new MyResource())

Or maybe see also 'How do you get *ant* to not print out javac warnings?'.

An IDE should give you the option to suppress a particular warning either globally or only for a single statement (without the annotation).


To expand on Radiodef's answer. I think RAII with try-with-resources is totally acceptable pattern for java. But to actually suppress warning

  • you need to use @SuppressWarnings("try") instead of @SuppressWarnings("unused").
  • And add annotation on method instead of variable declaration

An example with above points applied:

   @SuppressWarnings("try")
   void myMethod1() {
       try(MyResource myVar = new MyResource(..)) {
           //I am not using myVar here 
       }
   }

Expanding on the pattern itself. I've extensively used it to manage read-write locks and it worked great.

In my code I've used the trick to sometime preemptively unlock some resource to increase concurrency, like this:

try (Guard g1 = new Guard(myLock1)) {
    someStuffThatRequiresOnlyLock1();
    try (Guard g2 = new Guard(myLock2)) {
        someStuffThatRequiresBothLocks();
        if (isSomething) {
            g1.close();
            someMoreSuffThatRequiresOnlyLock2()
        } else {
            someMoreSuffThatRequiresBothLocks();
        }
    }
}

Locks are always acquired in the same order, but unlocking is performed as needed leaving as much space for concurrent processing as possible. The adjustment to make it work with read-write locks is to modify Guard class to allow repeated closings:

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();
        }
    }
}

Update: since Java 9 you do not need to suppress any warnings for this pattern in Java. You can reference a single effectively final variable in a try-clause, this will result in no warnings:

    Guard g1 = new Guard(myLock1);
    try (g1) {
        someStuffThatRequiresOnlyLock1();
        Guard g2 = new Guard(myLock2);
        try (g2) {
            someStuffThatRequiresBothLocks();
            if (isSomething) {
                g1.close();
                someMoreSuffThatRequiresOnlyLock2()
            } else {
                someMoreSuffThatRequiresBothLocks();
            }
        }
    }

  1. Is there a simple way for me to overcome this? I am thinking of splitting the constructor call into a simpler constructor and an instance method like this:

try(MyResource myVar = new Resource()) {
   myvar.Initialize()
   ...
}

Yes, @DesertIce, I use the technique you suggested. Although I'd encourage you to use a more descriptive name like preventCompilerWarning() which is obviously a stub, instead of initialize() which looks like it does something useful.

Another, possibly better solution is:

try(MyResource myVar = new Resource())
{
   assert(myVar != null); // Prevents compiler warning about unused var.
   ...
}

Using assert() with a comment is probably better because it:

  1. Is not compiled into production code.
  2. Works on classes you didn't write.

I got this solution from Hemal's comment on my blog post.

Sadly, @Radiodef's solution @SuppressWarnings("unused") MyResource doesn't work (any more). @VictorNazarov's @SuppressWarnings("try") solution works, but it seems a shame to suppress an entire method that might have multiple try blocks.

I'd love to see Java implement something like Kotlin that allows you to use _ instead of a variable name whenever you're forced to declare a variable you don't use - makes what you're doing explicit (and brief). Or at least recognize variables named ignored or unused or something.