There is a fairly common issue that arises in beginning Java programming exercises, around code like this:
try (var sc = new Scanner(System.in)) {
// use sc
}
This code closes sc when it's finished, which in turn closes System.in. If this code is called subsequently, it will fail because System.in is now closed.
This is driven by a misguided notion that "since you're opening a Scanner, you need to close it" thus indicating the use of try-with-resources. This reasoning is misguided because only a subset of Scanner constructors actually open a resource (the ones that take a File or Path) whereas others merely wrap an already-opened resource (the ones that take an InputStream, Readable, or ReadableByteChannel) and another (the constructor that takes a String) doesn't use any resources at all.
One could make an argument that if Scanner didn't actually open the resource, then closing the Scanner shouldn't close it. But this is at odds with all the wrapping stream classes in java.io. It's also an incompatible change, as any code relying on closing Scanner is also relying on it to close its underlying resource, regardless of how that resource was opened. Thus it falls to the caller to use try-with-resources only on Scanners that actually open resources and not to use try-with-resources on, or otherwise close the Scanner, in the cases where the Scanner is wrapping something they want to leave open -- like System.in.
However, the Scanner::close method specification is confusing because it discusses closing things that implement the Closeable interface. This is confusing because neither File nor Path implement Closeable but presumably Scanner opens resources using them and closes them properly.
There are a couple things that could be improved in the Scanner specification.
1) The specification should clarify when it's necessary to use Scanner with try-with-resources. For the Scanner(File) and Scanner(Path) constructors, the only object available to the user that contains the resource is the Scanner itself; thus that Scanner instance should be used with try-with-resources.
2) For the resource-wrapping constructors (InputStream, Readable, ReadableByteChannel) the caller needs to decide whether ownership of the resource is or is not passed to the Scanner. If the former, then the Scanner should be closed. If the latter, the Scanner should not be closed, and the caller needs to keep a reference to the wrapped resource for further reuse and is responsible for closing it elsewhere.
3) The behavior of Scanner::close should be clarified; the bit about Closeable is somewhat misleading. But it does seem to reflect reality, as some Readable instances implement Closeable and others do not, and Scanner indeed does a dynamic check. But the Scanner::close specification should be augmented to cover the File and Path cases.
try (var sc = new Scanner(System.in)) {
// use sc
}
This code closes sc when it's finished, which in turn closes System.in. If this code is called subsequently, it will fail because System.in is now closed.
This is driven by a misguided notion that "since you're opening a Scanner, you need to close it" thus indicating the use of try-with-resources. This reasoning is misguided because only a subset of Scanner constructors actually open a resource (the ones that take a File or Path) whereas others merely wrap an already-opened resource (the ones that take an InputStream, Readable, or ReadableByteChannel) and another (the constructor that takes a String) doesn't use any resources at all.
One could make an argument that if Scanner didn't actually open the resource, then closing the Scanner shouldn't close it. But this is at odds with all the wrapping stream classes in java.io. It's also an incompatible change, as any code relying on closing Scanner is also relying on it to close its underlying resource, regardless of how that resource was opened. Thus it falls to the caller to use try-with-resources only on Scanners that actually open resources and not to use try-with-resources on, or otherwise close the Scanner, in the cases where the Scanner is wrapping something they want to leave open -- like System.in.
However, the Scanner::close method specification is confusing because it discusses closing things that implement the Closeable interface. This is confusing because neither File nor Path implement Closeable but presumably Scanner opens resources using them and closes them properly.
There are a couple things that could be improved in the Scanner specification.
1) The specification should clarify when it's necessary to use Scanner with try-with-resources. For the Scanner(File) and Scanner(Path) constructors, the only object available to the user that contains the resource is the Scanner itself; thus that Scanner instance should be used with try-with-resources.
2) For the resource-wrapping constructors (InputStream, Readable, ReadableByteChannel) the caller needs to decide whether ownership of the resource is or is not passed to the Scanner. If the former, then the Scanner should be closed. If the latter, the Scanner should not be closed, and the caller needs to keep a reference to the wrapped resource for further reuse and is responsible for closing it elsewhere.
3) The behavior of Scanner::close should be clarified; the bit about Closeable is somewhat misleading. But it does seem to reflect reality, as some Readable instances implement Closeable and others do not, and Scanner indeed does a dynamic check. But the Scanner::close specification should be augmented to cover the File and Path cases.