-1

My goal is to understand on how to properly override close() method from java.io.Closeable.

I actually don't understand why some class like java.util.Scanner close() method only change the field closed to true and then on next() method it always do the ensureOpen() method before executing other method, essentially it makes me to think that close() method is just for the aesthetic aspect instead of to prevent memory leak.

I tried to create a test, based on assumption that close() method

Closeable Closes this stream and releases any system resources associated with it. If the stream is already closed then invoking this method has no effect.

My expected result: printer.println("should not print"); should fail.

My actual result: printer.println("should not print"); print the result.

Application.java

import java.io.IOException;

public class Application {
  public static void main(String[] args) {
    final Printer printer = new Printer();
    try (printer) {
      printer.println("hello");
    } catch (IOException e) {
      e.printStackTrace();
    }

    printer.println("should not print");

  }
}

Printer.java

public class Printer implements Closeable {
  public void println(String x) {
    System.out.println(x);
  }

  @Override
  public void close() throws IOException {
    System.out.println(Printer.class + " close");
  }
}

EDIT: Following the discussion with @Turing85, I am following the java.util.Scanner pattern to use flag-option

public class Printer implements Closeable {
  private boolean closed = false;

  private void ensureOpen() {
    if (this.closed)
      throw new IllegalStateException("Printer closed");
  }

  public void println(String x) {
    ensureOpen();
    System.out.println(x);
  }

  @Override
  public void close() throws IOException {
    this.closed = true;
    System.out.println("Printer closed");
  }
}
Jason Rich Darmawan
  • 1,607
  • 3
  • 14
  • 31
  • In `close()`, either call `System.out.close()` to close `System.out` (Attention: this will close `System.out`, itt cannot be reopened, hence for the remainder of the program execution, no output can be written to `System.out`) or set a flag, e.g. `boolean closed` (initialized with `false`) to `true`. If you go for the flag-option, only call `System.out.println(...)` in `println(...)` if `closed` is `false`. – Turing85 Aug 07 '21 at 12:35
  • @Turing85 does `close()` method is to kill `Printer` object OR to just prevent me to access e.g `Printer.println()` in the future after `close()` method, asumming I go with the `flag-option`? I am asumming it is better to do just `printer = null` than `printer.close()` if I wish to release resource, am I right? – Jason Rich Darmawan Aug 07 '21 at 12:39
  • I do not know what you mean by "*kill*". An object cannot be "killed". The semantics of `Closeable::close` are described in the [documentation](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/io/Closeable.html#close()). The documentation does say nothing about the behaviour wrt. subsequent calls to `println(...)` or alike. I would not set the `printer` to `null` without further changes since this would lead to a `NullPointerException` on a subsequent `println(...)`-call. – Turing85 Aug 07 '21 at 12:42
  • @Turing85 "kill" an object in this case to release the object from the memory, to make space for other object. Does `printer = null` will release the system resource associated with `printer` or this will cause memory leak because `printer = null` will only remove the reference? – Jason Rich Darmawan Aug 07 '21 at 12:48
  • Java does not work this way. For one, objects are not explicitly destroyed, but garbage-collected by the garbage collector if they are unreachable. For another, `System.out` is still reachable since... it is reachable through `System.out`. I believe you are overthinking things here. – Turing85 Aug 07 '21 at 12:50
  • @Turing85 Well noted. Thank you for the time. I will try to look for proper usage of implementing Closeable to prevent memory leak in the future before writing the question. – Jason Rich Darmawan Aug 07 '21 at 12:58
  • @Turing85 I have a theory of `java.io.Closeable` usage, please kindly check down below in the answer section, what do you think? – Jason Rich Darmawan Aug 07 '21 at 13:38

1 Answers1

-1

After some thought, coupled with dependency injection concept. I came into conclusion that implementation of java.io.Closeable is to prevent memory leak. Here's how you are going to use it.

You don't do this.

public class Printer {
  private final Scanner scanner = new Scanner(System.in);
}

Instead, you do this

public class Printer {
  private final Scanner scanner;

  public Printer(Scanner scanner) {
    this.scanner = scanner;
  }
}

And then you call it with

public class Application {
  public static void main(String[] args) {
    Printer printer = new Printer(new Scanner(System.in));

    // or maybe like this
    Scanner scanner = new Scanner(System.in);
    Printer printer = new Printer(scanner);

    // do something
    // e.g call method from the printer object that use scanner method.

    scanner.close();
    printer = null;
    scanner = null;
  }
}

I come to conclusion that If you create an object that have dependency of let's say, a class that will do IO operation, you use implement java.io.Closeable

So this is the simplest implementation of java.io.Closeable that I can think of to prevent memory leak.

Printer.java

package Closeable;

import java.io.Closeable;
import java.io.IOException;
import java.util.Scanner;

public class Printer implements Closeable {
  private boolean closed = false;

  private Scanner scanner;

  public Printer(Scanner scanner) {
    this.scanner = scanner;
  }

  private void ensureOpen() {
    if (this.closed)
      throw new IllegalStateException("Printer closed");
  }

  public void next() {
    ensureOpen();
    System.out.println(scanner.next());
  }

  @Override
  public void close() throws IOException {
    scanner.close();
    this.closed = true;
    System.out.println("Printer closed");
  }
}

And you use it like this.

public class Application {
  public static void main(String[] args) {
    final Printer printer = new Printer(new Scanner(System.in));
    try (printer) {
      printer.next();
    } catch (IOException e) {
      e.printStackTrace();
    }

    printer.next();

  }
}
Jason Rich Darmawan
  • 1,607
  • 3
  • 14
  • 31
  • 2
    `Closeable` has nothing to do with memory leaks. In fact, it is [quite hard to create an actual memory leak in java](https://stackoverflow.com/questions/6470651/how-can-i-create-a-memory-leak-in-java). `Closeable` is more about limited resources that should be returned to the OS ([as stated in the documentation](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/io/Closeable.html)). – Turing85 Aug 07 '21 at 13:42