Search code examples
javaexceptiontry-catchtry-catch-finally

try-with-resource for resource as a class member


I try to use a class member as a resource in the try block (Because that resource needs to call close() after try block):

class Manager {
    MyResource m_myResource;
    ...

    void doTraining() {
        m_myResource = createMyResource();
        try(m_myResource) {
            ...
        }
    }
    
}

But there will be a complain saying that m_myResource is not effective final. Why? I think m_myResourceis not changed after initialization. Anyway, not sure how to fix this issue. One way I can think of is:

class Manager {
    MyResource m_myResource;
    ...

    void doTraining() {
        MyResource newResource = createMyResource();
        m_myResource = newResource;
        try(m_myResource) {
            ...
        }
    }
}

But I am not sure if assigning the local resource to class member could cause any problem?


Solution

  • Maintaining a field for storing an object of limited lifetime should be avoided. But if you really need it, the code should look like

    class Manager {
        MyResource m_myResource;
        ...
    
        void doTraining() {
            try(MyResource newResource = createMyResource()) {
                m_myResource = newResource;
                ...
            }
            finally {
                m_myResource = null;
            }
        }
    }
    

    So when there is no ongoing operation within doTraining()’s try() block, the field is always null.

    It’s clear that this implementation of doTraining is not reentrant; there must be no overlapping invocations of this method, neither through multiple threads nor through recursive calls.

    As said, this is not recommended. But sometimes, passing the resource through all API layers used in an operation is impractical. The existence of such scenarios has been recognized and there’s a planned general solution, ExtentLocal.

    With this, the future version would look like

    class Manager {
        final static ExtentLocal<MyResource> MY_RESSOURCE = new ExtentLocal<>();
        ...
    
        void doTraining() {        
            try(MyResource newResource = createMyResource()) {
                ExtentLocal.where(MY_RESSOURCE, newResource )
                    .run(() -> {/* code indirectly invoking foo */});
                ...
            }
        }
        void foo() {
            var resource = MY_RESSOURCE.get();
            // use resource
        }
    }
    

    Note how this does not mutate the instance for passing the locally valid resource. So here, doTraining() is reentrant.

    But well, this is somewhere in the future…