Ok so this turns out to be a pretty old feature and well documented, I just didn't see it.
The feature is called The try-with-resources Statement.
I keep @shmily as accepted answer since, is correct and it responds the question (and was also the first), I'll just add here a bit since it may help, and especially I'll add the code for my use-case that I needed it.
Also, read the last note about what user 'Hovercraft Full Of Eels' said regarding my implementation
So at it's simpler form, we can use it as
public static void main( String... args )
{
try (Scanner reader = new Scanner(System.in)) {
System.out.print(">>> username: ");
if (reader.hasNextLine()) {
String username = reader.nextLine();
System.out.format("Hello %s", username);
}
} catch (NoSuchElementException | IllegalStateException error) {
System.out.println("Error while reading scanner");
error.printStackTrace();
}
}
Now, following this docs of baeldung.com/java-try-with-resources it shows that you can actually declared it outside, as long is either final.
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
// omitted
}
Now, for my particular use case, and first evaluation though, I would not recommended always (since, actually even in their example, the PrintWriter may thrown an error FileNotFoundException) so it makes sense if that error is catch before-hand or is expected. else I see that you can do a variation
public static void main( String... args )
{
String fileName = "output.txt";
final Scanner readerTerminal = new Scanner(System.in);
try (
readerTerminal;
PrintWriter writer = new PrintWriter("output.txt");
) {
System.out.print(">>> username: ");
if (readerTerminal.hasNextLine()) {
String username = readerTerminal.nextLine();
System.out.format("Hello %s", username);
writer.write(username);
}
} catch (FileNotFoundException error) {
System.out.format("File %s not found, reason: %s", fileName, error);
error.printStackTrace();
} catch (NoSuchElementException | IllegalStateException error) {
System.out.format("Error while reading scanner, reason: %s", error);
error.printStackTrace();
} catch (Exception error) {
System.out.format("Something went wrong while reading scanner, reason: %s", error);
error.printStackTrace();
}
}
This especially makes sense, if the reader
comes from the function parameters.
Having said that, this feature is *very very very similar to python context manager, you can define/custom your own class that implements AutoCloseable
.
public class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Closed MyResource");
}
}
As longs as it calls the close
, so you can do whatever you want with it (any kind of clean-up, resource closing, api closing and what more what else).
The only difference I see (at least from user perspective, obviously the implementation may be very different on each language) is that, to enable this feature in java
you really need to put it inside a try/catch block, whereas, in python
is completely independent of an error - catch scenario.
Why is this relevant?
For what I see, having it as a try-catch scenario may seems to restrict what you can actually do with it and especially, you wanna close that resource not because an error was raised, but because it existed the scope!, which is in fact how it works, but it may make you think that, it closes only when is raised.
In the following example I show how to use it without the need for an exception to be thrown, but, it doesn't look as obvious as in the python way to be honest (which is not the end of the world).
Normal AutoCloseable
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args )
{
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
} catch (Exception error) {
error.printStackTrace();
}
}
}
Mhh, but what if I don't wanna thrown an error ?
And I just want to call some other thing that, I want to make sure are called when that object is closed.
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args )
{
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
}
}
}
Do it this way, it's looks very similar to the python version, I don't have to catch (and throw) anything since, that is not the point my particular autoclosable,
Cool thing is that, any un-caught erorrs that happened withing that try-with-resource will still call the close method
public class Main
{
public static class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Resource closed, let's call some resource ... ");
}
}
public static void main( String[] args ) throws FileNotFoundException {
final MyResource resource1 = new MyResource();
try (resource1; ) {
System.out.println("Inside Try - catch block");
final Scanner readerTerminal = new Scanner(new File("dont_exits.txt"));
}
}
}
Which outputs
Inside Try - catch block
Resource closed, let's call some resource ...
Exception in thread "main" java.io.FileNotFoundException: dont_exits.txt (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.util.Scanner.<init>(Scanner.java:639)
at com.koubae.app1.Main.main(Main.java:30)
Process finished with exit code 1
'Hovercraft Full Of Eels' note on comment
Java has try-with-resources, which is probably the best way, except in certain circumstances, one of which you are using System.in (as you are doing) and wish to avoid closing it prematurely. Once closed, that stream remains closed.
So, despite all the above example, I don't think you should close at a Scanner which scans the System.in
(but there may be reasons)
public static void main( String[] args )
{
final Scanner readerUserTerminal = new Scanner(System.in);
try (
readerUserTerminal;
) {
System.out.print(">>> username: ");
if (readerUserTerminal.hasNextLine()) {
String username = readerUserTerminal.nextLine();
System.out.format("Hello %s", username);
}
} catch (NoSuchElementException | IllegalStateException error) {
System.out.format("Error while reading scanner, reason: %s", error);
error.printStackTrace();
} catch (Exception error) {
System.out.format("Something went wrong while reading scanner, reason: %s", error);
error.printStackTrace();
}
// yea no ... will break...
Scanner second = new Scanner(System.in);
System.out.print(">>> username: ");
String username2 = second.nextLine();
System.out.format("Hello %s", username2);
}
output
Hello fede>>> username: Exception in thread "main" java.util.NoSuchElementException: No line found
at java.base/java.util.Scanner.nextLine(Scanner.java:1651)
at com.koubae.app1.Main.main(Main.java:39)
So be careful!, I am actually surpise since, in many many tutorials they are using the Scanner to read user input from terminal and then show to close it, wich, not being a File, but the System.in (for me is like stdin
like in C or python sys.stdin
. Quite awful the level of tutorials out there. watch out! :D