Home > Blockchain >  How to handle Autoclosable Resources which being passed as a Method arguments
How to handle Autoclosable Resources which being passed as a Method arguments

Time:11-06

I'm trying to migrate existing code to try with resource, but not sure how to deal when the closable resources are passed to other methods.

  1. Is passing around a closeable a resource good practice?

  2. How to prevent the dev touching the method additionalMethod from closing the resource by mistake.

  3. how should such cases be handled?

example code:

class SomeClass{
    void readMethod(){
        try (Scanner scanner = new Scanner(new File("test.txt"))) {
            additionalMethod(scanner);
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        }
    }
    void additionalMethod(Scanner scanner){ // < is this a good practise?
       while (scanner.hasNext()) {
            System.out.println(scanner.nextLine());
       }
     // scanner.close() how to ensure no one does this by mistake
    }
}

CodePudding user response:

Passing closeable resources around is fine. Let’s look at some examples in Java SE:

As for how to prevent other methods from closing a closeable resource, you may want to use assertions in the calling code:

void readMethod() {
    try (ReadableByteChannel channel = FileChannel.open(Path.of("test.txt"));
         Scanner scanner = new Scanner(channel)) {

        additionalMethod(scanner);
        assert channel.isOpen() :
            "additionalMethod was not supposed to close the Scanner!";
    } catch (IOException e) {
        e.printStackTrace();
    }
}

void additionalMethod(Scanner scanner) {
   while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
   }
}

CodePudding user response:

Closing a closed Stream has no effect

Is passing around a closeable a resource good practise?

How to prevent the dev touching the method additionalMethod from closing the resource by mistake.

There's nothing bad with opening the stream in one method, where it's wrapped in try-with-resources in handing it out to another method, like in your example.

If you call close() on a stream multiple times, nothing would fail. The first call would close the stream, the subsequent calls would have no effect.

Here's a quote from the documentation of the Reader interface:

Closes the stream and releases any system resources associated with it. Once the stream has been closed, further read(), ready(), mark(), reset(), or skip() invocations will throw an IOException. Closing a previously closed stream has no effect.

BufferedReader vs Scanner

Hovewhever, using Scanner for dialing with files is not the most performant approach, you can use FileReader wrapped with a BufferedReader which has a greater capacity of the buffer than Scanner (8192 vs 1024).

public void readMethod(String fileName) {
    try (var reader = new BufferedReader(new FileReader(fileName))) {
        additionalMethod(reader);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void additionalMethod(BufferedReader reader) throws IOException {
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    reader.close(); // it's pointless but not harmful
    reader.close(); // it's pointless but not harmful
}

NIO.2

Methods for handling Input/Output became way learner if you're using NIO.2 API.

That how these two methods could be reimplemented using [Files.lines()] which produces a stream of lines:

public void readMethod(String fileName) {
    try (Stream<String> stream = Files.lines(Path.of(fileName), Charset.defaultCharset())) {
        additionalMethod(stream);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void additionalMethod(Stream<String> stream) {
    stream.forEach(System.out::println);
}

CodePudding user response:

  1. You can wrap scanner into another class which do not expos the close method. Using this you can pass around the closeable object because of the fact that the methods you pass do not have access to close method.
  2. You should close the resource when you are done, in the example you give it will be closed safely. All you need is to wrap closeable object into another and pass the wrapper around which do not expose close method.

NOTE: In the example you give, all is happening in the same class. Hence, it is not that much of a problem. Problem happens when you pass it to an alien method. (Method specified in another class)

public class Wrapper{ //probabily there is better name depending on the domain, application and context.
   private final Scanner underlyingScannner;

   public Wrapper(Scanner scanner){
      this.underlyingScannner = scanner;
   }

   public boolean hasNext(){
     return underlyingScannner.hasNext();
   }

   public String nextLine(){
     return underlyingScannner.nextLine();
   }

}

  1. If you have an interface which the closeable objects implements and you can use in an alien method. You can pass using that interface instead of creating an explicit wrapper. For instance;

    public class ACloseableObject implements ObjectInterface, AutoCloseable{ ... }
    

In this case, you can pass using the reference of type ObjectInterface. (You do not need to create a seperate wrapper because of the fact that the closeable object already implements ObjectInterface and this interface do not expose close operation)

NOTE: In this case, the object could be casted into AutoCloseable.

final AutoCloseable casted = (AutoCloseable)objectInterfaceReference;
  • Related