<div dir="auto">Hello Tzengshinfu,<div dir="auto">Unfortunately completely automatic cleanup is not a solved problem, so "as shown bellow" can't work (the 2 main problems are fields and concurrency).</div><div dir="auto"><br></div><div dir="auto">The best we can hope for is to have a set of modifiers to `new` and let the compiler throw a compile time exception if we don't specify the modifier (e.g. `new scoped Buffer()`, `new manual Buffer()`, ...), but this also has problems (it is annoying).</div><div dir="auto"><br></div><div dir="auto">Also the problem if "fluent cleanup" is not the only problem we have with the current TwR.</div><div dir="auto"><br></div><div dir="auto">Because of the complexity of the subject I don't think this discussion will go anywhere in here.</div><div dir="auto">I believe that a separate discussion should be opened in amber-spec-experts, I also don't know the current "free hands" to work on anything in this subject, but this is a different question.</div><br><br><div class="gmail_quote" dir="auto"><div dir="ltr" class="gmail_attr">On Wed, Sep 27, 2023, 11:24 tzengshinfu <<a href="mailto:tzengshinfu@gmail.com" target="_blank" rel="noreferrer">tzengshinfu@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi, Folks:<br>
<br>
Since I previously used C# (to the extent that I consumed syntactic<br>
sugar until I almost got "syntax diabetes"), I unintentionally brought<br>
my habits into the Java world. However, I now recognize the<br>
differences in their design philosophies.<br>
<br>
In fact, I agree with Tagir's opinion. TWR (Try-With-Resources) is<br>
indeed a more explicit and reliable way of automatic resource cleanup.<br>
However, when I saw JEP 445: Unnamed Classes and Instance Main<br>
Methods, which bears similarities to C# Top-level statements, and<br>
noticed that Java chose Virtual Threads instead of async/await, I<br>
realized that "less is more, simplification is the future direction;<br>
we should make the compiler do more, and we should focus on business<br>
logic. So, wouldn't it be better if we didn't have to manage the<br>
timing of resource release?" That's the basis for my suggestion.<br>
<br>
Holo's opinion is excellent. The awkward thing is that C# chose to<br>
turn the using statement into a using declaration version, while Java<br>
chose to add resource management functionality to try-catch-finally.<br>
So, any modification would break the semantics of try, but I'm also<br>
concerned that since try-with-resources already exists, there may be<br>
little room for further changes in the automatic cleanup mechanism.<br>
<br>
I also agree with Brian's opinion. `defer` might behave unexpectedly<br>
due to different scope levels, and developers might lump logic outside<br>
of resource cleanup into it, leading to dreadful results. Instead of<br>
desiring the delayed execution effect of `defer`, I think we want "a<br>
more automated automatic cleanup mechanism" as shown below:<br>
<br>
```java<br>
public static void main(String... args) throws FileNotFoundException,<br>
IOException {<br>
BufferedReader reader = new BufferedReader(new<br>
FileReader("studentName.txt"));<br>
String studentName = "not found";<br>
<br>
while ((studentName = reader.readLine()) != null) {<br>
break;<br>
}<br>
<br>
System.out.println(studentName);<br>
/* The BufferedReader 'reader' will be automatically cleaned up<br>
before the method exits, even without explicit TWR indication. */<br>
}<br>
```<br>
<br>
Thank you for taking the time to provide thoughtful responses from<br>
various perspectives.<br>
<br>
<br>
/* GET BETTER EVERY DAY */<br>
<br>
<br>
<br>
tzengshinfu <<a href="mailto:tzengshinfu@gmail.com" rel="noreferrer noreferrer" target="_blank">tzengshinfu@gmail.com</a>> 於 2023年9月25日 週一 下午2:10寫道:<br>
><br>
> Hi, Folks,<br>
><br>
> Since my last suggestion,<br>
> I've conducted some research and stumbled upon the<br>
> `@Cleanup`[<a href="https://projectlombok.org/features/Cleanup" rel="noreferrer noreferrer noreferrer" target="_blank">https://projectlombok.org/features/Cleanup</a>] feature in the<br>
> popular library `Lombok`.<br>
><br>
> Personally, I believe that the existence of this feature indicates a<br>
> certain demand, but it's unfortunate that it requires importing the<br>
> library, which adds to the maintenance complexity.<br>
> I'd like to hear your thoughts on this situation. Is importing Lombok<br>
> the best approach?<br>
><br>
> P.S. This is my second email to the mail list. If there's anything I<br>
> may have overlooked or done incorrectly, please feel free to let me<br>
> know. Thank you.<br>
><br>
> Thanks for your interest and support,<br>
> Hsinfu Tseng<br>
><br>
> /* GET BETTER EVERY DAY */<br>
><br>
><br>
> tzengshinfu <<a href="mailto:tzengshinfu@gmail.com" rel="noreferrer noreferrer" target="_blank">tzengshinfu@gmail.com</a>> 於 2023年9月4日 週一 上午11:14寫道:<br>
> ><br>
> > Hi, Folks:<br>
> ><br>
> > Recently, I've been using `JBang` to write some script tools, and I<br>
> > feel that Java has transformed with modern syntax features like<br>
> > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly<br>
> > added language features provide developers with more options in<br>
> > specific scenarios, almost like giving Java a set of wings.<br>
> ><br>
> > When using the `try-with-resources statement`, I thought it might be<br>
> > possible to introduce an additional simplified syntax, which I believe<br>
> > would be useful when developing simple, small-scale programs.<br>
> > Meanwhile, scenarios requiring resource closure timing specification<br>
> > or exception handling can still make use of the original syntax.<br>
> ><br>
> > At first glance, this simplified syntax may seem a bit unusual, but<br>
> > considering that `switch` itself encompasses both statements and<br>
> > expressions, and that `switch<br>
> > expression`[<a href="https://openjdk.org/jeps/361" rel="noreferrer noreferrer noreferrer" target="_blank">https://openjdk.org/jeps/361</a>] also adds a semicolon at the<br>
> > end of `}`, perhaps it won't be as difficult to accept.<br>
> ><br>
> > Because it reduces indentation, making the logic clearer and more<br>
> > visible, users don't need to consider resource closure timing; they<br>
> > can focus solely on business logic. It's possible that in the future,<br>
> > the likelihood of using this simplified syntax will increase.<br>
> ><br>
> > In my personal opinion, the original `try-with-resources statement`<br>
> > feels like driving a manual transmission car, where I have full<br>
> > control over everything. However, if we can let the runtime manage it<br>
> > autonomously, it's like driving an automatic car on the long journey<br>
> > of coding, and even more effortless.<br>
> ><br>
> > If there could be an alternative approach that is both hassle-free and<br>
> > doesn't require altering Java specifications, I hope that experienced<br>
> > individuals can share their insights.<br>
> ><br>
> ><br>
> > Thanks for your interest and support,<br>
> > Hsinfu Tseng<br>
> ><br>
> > (Using the JEP template because I've found it helps clarify my thoughts.)<br>
> ><br>
> ><br>
> > ## Summary<br>
> ><br>
> > Allow omitting the definition of scope when declaring<br>
> > `try-with-resources statement`.<br>
> ><br>
> ><br>
> > ## Goals<br>
> ><br>
> > * Not introducing new scopes to reduce the cognitive burden on<br>
> > developers, especially beginners.<br>
> > * Reducing the interference of hierarchical indentation to enhance<br>
> > code readability.<br>
> > * The declaration position of variables is not restricted by<br>
> > `try-with-resources statement`.<br>
> > * Ensure that resources are released before the current scope ends.<br>
> ><br>
> ><br>
> > ## Motivation<br>
> ><br>
> > The `try-with-resources statement` simplified resource management in<br>
> > Java after version 7. However, its side effect is the necessity to<br>
> > declare a scope and, at the same time, it restricts lifetime of the<br>
> > variable declared within the scope:<br>
> ><br>
> > ```java<br>
> > public class example1 {<br>
> > public static void main(String... args) throws<br>
> > FileNotFoundException, IOException {<br>
> > try (BufferedReader reader = new BufferedReader(new<br>
> > FileReader("studentName.txt"))) {<br>
> > String studentName = "not found";<br>
> > /* Variable 'studentName' is only alive in the scope of<br>
> > try-with-resources statement. */<br>
> ><br>
> > while ((studentName = reader.readLine()) != null) {<br>
> > break;<br>
> > }<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > /* Variable 'studentName' cannot be resolved here. */<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > While it's possible to move variable declaration before<br>
> > `try-with-resources statement` and modify variable content within the<br>
> > scope of `try-with-resources statement`, the actions of declaration,<br>
> > assignment, and usage of variables are segmented by the scope of<br>
> > `try-with-resources statement`. In practice, we would prefer variable<br>
> > declarations to be as close as possible to their usage locations and<br>
> > avoid unnecessary<br>
> > separation[<a href="https://rules.sonarsource.com/java/RSPEC-1941/" rel="noreferrer noreferrer noreferrer" target="_blank">https://rules.sonarsource.com/java/RSPEC-1941/</a>]:<br>
> ><br>
> > ```java<br>
> > public class example2 {<br>
> > public static void main(String... args) throws<br>
> > FileNotFoundException, IOException {<br>
> > String studentName = "not found";<br>
> ><br>
> > // #region<br>
> > // The scope of try-with-resources statement separates<br>
> > // the declaration, assignment, and usage of variable 'studentName'.<br>
> > try (BufferedReader reader = new BufferedReader(new<br>
> > FileReader("studentName.txt"))) {<br>
> > while ((studentName = reader.readLine()) != null) {<br>
> > break;<br>
> > }<br>
> > }<br>
> > // #endregion<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > Furthermore, methods that involve multiple resources requiring<br>
> > management and dependencies might become cumbersome in terms of syntax<br>
> > due to `nested try-with-resources statements`:<br>
> ><br>
> > ```java<br>
> > public class example3 {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password)) {<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > try (PreparedStatement statement =<br>
> > conn.prepareStatement(selectSQL)) {<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery()) {<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > This proposal introduces a shorthand declaration that eliminates the<br>
> > scope limitations of `try-with-resources statement`. It enables<br>
> > variable declaration, assignment, and usage within the same scope,<br>
> > reduces indentation levels for improved code readability (particularly<br>
> > in `nested try-with-resources statements`), and defers the timing of<br>
> > resource release to the system, ensuring resource disposal before the<br>
> > scope in `try-with-resources statement` is declared ends. (Similar to<br>
> > Golang's `Defer statement` + resource<br>
> > close[<a href="https://go.dev/tour/flowcontrol/12" rel="noreferrer noreferrer noreferrer" target="_blank">https://go.dev/tour/flowcontrol/12</a>], C#'s `using<br>
> > declarations`[<a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using" rel="noreferrer noreferrer noreferrer" target="_blank">https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using</a>],<br>
> > and Swift's `Defer statement` + resource<br>
> > close[<a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement" rel="noreferrer noreferrer noreferrer" target="_blank">https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement</a>])<br>
> ><br>
> ><br>
> > ## Description<br>
> ><br>
> > The format of the `simplified try-with-resources statement` is as follows:<br>
> ><br>
> > ```java<br>
> > try (resources);/* Change '{}' to ';'. */<br>
> ><br>
> > ```<br>
> ><br>
> > It can be observed that omitting `{}` reduces the indentation, thus<br>
> > enhancing code readability by not introducing new scopes. Moreover,<br>
> > variable declarations can be positioned either before or after the<br>
> > `simplified try-with-resources statement`:<br>
> ><br>
> > ```java<br>
> > public class example1_simplified {<br>
> > public static void main(String... args) throws<br>
> > FileNotFoundException, IOException {<br>
> > try (BufferedReader reader = new BufferedReader(new<br>
> > FileReader("studentName.txt")));<br>
> > String studentName = "not found";<br>
> ><br>
> > while ((studentName = reader.readLine()) != null) {<br>
> > break;<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > Because it automatically converts the `simplified try-with-resources<br>
> > statement` to an equivalent `try-with-resources statement` after<br>
> > compilation:<br>
> ><br>
> > ```java<br>
> > public class example1_converted {<br>
> > public static void main(String... args) throws<br>
> > FileNotFoundException, IOException {<br>
> > try (BufferedReader reader = new BufferedReader(new<br>
> > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */<br>
> > String studentName = "not found";<br>
> ><br>
> > while ((studentName = reader.readLine()) != null) {<br>
> > break;<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }/* Add '}' before the current scope ends. */<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > Because the scope of `simplified try-with-resources statement` extends<br>
> > until the current scope is terminated, the variable `studentName`,<br>
> > which originally couldn't be resolved in `example1`, can now be<br>
> > resolved successfully. This allows variable declaration, assignment,<br>
> > and usage within the same scope, ensuring that resource close<br>
> > operations occur before the current scope ends.<br>
> ><br>
> > Invoking multiple dependent `simplified try-with-resources statements`<br>
> > is similar to regular variable declarations, thereby avoiding<br>
> > excessive nesting.<br>
> ><br>
> > ```java<br>
> > public class example3_simplified {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password));<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > try (PreparedStatement statement = conn.prepareStatement(selectSQL));<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery());<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > It will be converted to equivalent `nested try-with-resources<br>
> > statements` after compilation:<br>
> ><br>
> > ```java<br>
> > public class example3_converted {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password)) {/* Change ';' back to '{'. */<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > try (PreparedStatement statement =<br>
> > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery()) {/* Change<br>
> > ';' back to '{'. */<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }}}}/* Add the corresponding number (in this case, 3) of '}'<br>
> > before the current scope ends. */<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > After code formatting:<br>
> ><br>
> > ```java<br>
> > public class example3_formatted {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password)) {<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > try (PreparedStatement statement =<br>
> > conn.prepareStatement(selectSQL)) {<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery()) {<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > Comparing the code structures of `example3_simplified` and<br>
> > `example3_formatted`, it is evident that the usage of `simplified<br>
> > try-with-resources statement` enhances code readability by<br>
> > significantly reducing nested indentation levels. Additionally, it can<br>
> > be observed that in the converted `nested try-with-resources<br>
> > statements`, resource close actions will be executed in reverse order<br>
> > of declaration, just before the current scope ends.<br>
> ><br>
> > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods<br>
> > (Preview)`[<a href="https://openjdk.org/jeps/445" rel="noreferrer noreferrer noreferrer" target="_blank">https://openjdk.org/jeps/445</a>], it becomes convenient to<br>
> > create a simple, small-scale program:<br>
> ><br>
> > ```java<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > void main() throws SQLException {<br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password));<br>
> > try (PreparedStatement statement = conn.prepareStatement(selectSQL));<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery());<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> ><br>
> > ## Alternatives<br>
> ><br>
> > * One approach to mitigate the impact on readability caused by deeply<br>
> > `nested try-with-resources statements` is to consolidate multiple<br>
> > resources within the same `try-with-resources statement` as much as<br>
> > possible. However, formatting multiple resources with line breaks can<br>
> > also disrupt visual clarity, and furthermore, it's not always feasible<br>
> > to close many resources simultaneously:<br>
> ><br>
> > ```java<br>
> > public class alternative1 {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password);<br>
> > PreparedStatement statement =<br>
> > conn.prepareStatement(selectSQL)) {<br>
> > /* In this case, there are only 2 resources, but if there are 3 or more,<br>
> > it will become even less aesthetically pleasing. */<br>
> ><br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery()) {<br>
> > /* ResultSet 'result' cannot be closed simultaneously with<br>
> > PreparedStatement 'statement'<br>
> > because PreparedStatement 'statement' needs parameter<br>
> > configuration first. */<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > return studentName;<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > * Alternatively, creating a separate method to confine the scope of<br>
> > managed resources within the method can be done, but it necessitates<br>
> > 'naming' the new<br>
> > method[<a href="https://www.karlton.org/2017/12/naming-things-hard/" rel="noreferrer noreferrer noreferrer" target="_blank">https://www.karlton.org/2017/12/naming-things-hard/</a>]. This<br>
> > approach simply shifts complexity to the interior of additional<br>
> > method, which may not be conducive to creating simple, small-scale<br>
> > programs:<br>
> ><br>
> > ```java<br>
> > public class alternative2 {<br>
> > public static void main(String... args) throws SQLException {<br>
> > String selectSQL = "selectSQL";<br>
> > String studentID = "studentID";<br>
> > String studentName = getStudentName(selectSQL, studentID);<br>
> ><br>
> > System.out.println(studentName);<br>
> > }<br>
> ><br>
> > public static String getStudentName(String sql, String studentID)<br>
> > throws SQLException {<br>
> > String jdbcUrl = "jdbcUrl";<br>
> > String username = "username";<br>
> > String password = "password";<br>
> ><br>
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,<br>
> > username, password)) {<br>
> > try (PreparedStatement statement = conn.prepareStatement(sql)) {<br>
> > statement.setString(1, studentID);<br>
> ><br>
> > try (ResultSet result = statement.executeQuery()) {<br>
> > String studentName = "not found";<br>
> ><br>
> > while (result.next()) {<br>
> > studentName = result.getString(1);<br>
> > }<br>
> ><br>
> > return studentName;<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> > }<br>
> ><br>
> > ```<br>
> ><br>
> > /* GET BETTER EVERY DAY */<br>
</blockquote></div></div>