Idea[Simplified try-with-resources statement]
Holo The Sage Wolf
holo3146 at gmail.com
Mon Sep 25 10:06:11 UTC 2023
The discussion of improved TwR came before several times, and as always I
think this is a great idea.
A small problem I have with the proposal is that it breaks the semantics of
"try", as you have no way to catch.
I would in general prefer to have a plan for a complete new stronger
enhanced auto-cleanup mechanism and only then break it into small JEPs
On Mon, Sep 25, 2023, 11:18 tzengshinfu <tzengshinfu at gmail.com> wrote:
> Hi, Folks,
>
> Since my last suggestion,
> I've conducted some research and stumbled upon the
> `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the
> popular library `Lombok`.
>
> Personally, I believe that the existence of this feature indicates a
> certain demand, but it's unfortunate that it requires importing the
> library, which adds to the maintenance complexity.
> I'd like to hear your thoughts on this situation. Is importing Lombok
> the best approach?
>
> P.S. This is my second email to the mail list. If there's anything I
> may have overlooked or done incorrectly, please feel free to let me
> know. Thank you.
>
> Thanks for your interest and support,
> Hsinfu Tseng
>
> /* GET BETTER EVERY DAY */
>
>
> tzengshinfu <tzengshinfu at gmail.com> 於 2023年9月4日 週一 上午11:14寫道:
> >
> > Hi, Folks:
> >
> > Recently, I've been using `JBang` to write some script tools, and I
> > feel that Java has transformed with modern syntax features like
> > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly
> > added language features provide developers with more options in
> > specific scenarios, almost like giving Java a set of wings.
> >
> > When using the `try-with-resources statement`, I thought it might be
> > possible to introduce an additional simplified syntax, which I believe
> > would be useful when developing simple, small-scale programs.
> > Meanwhile, scenarios requiring resource closure timing specification
> > or exception handling can still make use of the original syntax.
> >
> > At first glance, this simplified syntax may seem a bit unusual, but
> > considering that `switch` itself encompasses both statements and
> > expressions, and that `switch
> > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the
> > end of `}`, perhaps it won't be as difficult to accept.
> >
> > Because it reduces indentation, making the logic clearer and more
> > visible, users don't need to consider resource closure timing; they
> > can focus solely on business logic. It's possible that in the future,
> > the likelihood of using this simplified syntax will increase.
> >
> > In my personal opinion, the original `try-with-resources statement`
> > feels like driving a manual transmission car, where I have full
> > control over everything. However, if we can let the runtime manage it
> > autonomously, it's like driving an automatic car on the long journey
> > of coding, and even more effortless.
> >
> > If there could be an alternative approach that is both hassle-free and
> > doesn't require altering Java specifications, I hope that experienced
> > individuals can share their insights.
> >
> >
> > Thanks for your interest and support,
> > Hsinfu Tseng
> >
> > (Using the JEP template because I've found it helps clarify my thoughts.)
> >
> >
> > ## Summary
> >
> > Allow omitting the definition of scope when declaring
> > `try-with-resources statement`.
> >
> >
> > ## Goals
> >
> > * Not introducing new scopes to reduce the cognitive burden on
> > developers, especially beginners.
> > * Reducing the interference of hierarchical indentation to enhance
> > code readability.
> > * The declaration position of variables is not restricted by
> > `try-with-resources statement`.
> > * Ensure that resources are released before the current scope ends.
> >
> >
> > ## Motivation
> >
> > The `try-with-resources statement` simplified resource management in
> > Java after version 7. However, its side effect is the necessity to
> > declare a scope and, at the same time, it restricts lifetime of the
> > variable declared within the scope:
> >
> > ```java
> > public class example1 {
> > public static void main(String... args) throws
> > FileNotFoundException, IOException {
> > try (BufferedReader reader = new BufferedReader(new
> > FileReader("studentName.txt"))) {
> > String studentName = "not found";
> > /* Variable 'studentName' is only alive in the scope of
> > try-with-resources statement. */
> >
> > while ((studentName = reader.readLine()) != null) {
> > break;
> > }
> > }
> >
> > System.out.println(studentName);
> > /* Variable 'studentName' cannot be resolved here. */
> > }
> > }
> >
> > ```
> >
> > While it's possible to move variable declaration before
> > `try-with-resources statement` and modify variable content within the
> > scope of `try-with-resources statement`, the actions of declaration,
> > assignment, and usage of variables are segmented by the scope of
> > `try-with-resources statement`. In practice, we would prefer variable
> > declarations to be as close as possible to their usage locations and
> > avoid unnecessary
> > separation[https://rules.sonarsource.com/java/RSPEC-1941/]:
> >
> > ```java
> > public class example2 {
> > public static void main(String... args) throws
> > FileNotFoundException, IOException {
> > String studentName = "not found";
> >
> > // #region
> > // The scope of try-with-resources statement separates
> > // the declaration, assignment, and usage of variable
> 'studentName'.
> > try (BufferedReader reader = new BufferedReader(new
> > FileReader("studentName.txt"))) {
> > while ((studentName = reader.readLine()) != null) {
> > break;
> > }
> > }
> > // #endregion
> >
> > System.out.println(studentName);
> > }
> > }
> >
> > ```
> >
> > Furthermore, methods that involve multiple resources requiring
> > management and dependencies might become cumbersome in terms of syntax
> > due to `nested try-with-resources statements`:
> >
> > ```java
> > public class example3 {
> > public static void main(String... args) throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password)) {
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > try (PreparedStatement statement =
> > conn.prepareStatement(selectSQL)) {
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery()) {
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > System.out.println(studentName);
> > }
> > }
> > }
> > }
> > }
> >
> > ```
> >
> > This proposal introduces a shorthand declaration that eliminates the
> > scope limitations of `try-with-resources statement`. It enables
> > variable declaration, assignment, and usage within the same scope,
> > reduces indentation levels for improved code readability (particularly
> > in `nested try-with-resources statements`), and defers the timing of
> > resource release to the system, ensuring resource disposal before the
> > scope in `try-with-resources statement` is declared ends. (Similar to
> > Golang's `Defer statement` + resource
> > close[https://go.dev/tour/flowcontrol/12], C#'s `using
> > declarations`[
> https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using
> ],
> > and Swift's `Defer statement` + resource
> > close[
> https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement
> ])
> >
> >
> > ## Description
> >
> > The format of the `simplified try-with-resources statement` is as
> follows:
> >
> > ```java
> > try (resources);/* Change '{}' to ';'. */
> >
> > ```
> >
> > It can be observed that omitting `{}` reduces the indentation, thus
> > enhancing code readability by not introducing new scopes. Moreover,
> > variable declarations can be positioned either before or after the
> > `simplified try-with-resources statement`:
> >
> > ```java
> > public class example1_simplified {
> > public static void main(String... args) throws
> > FileNotFoundException, IOException {
> > try (BufferedReader reader = new BufferedReader(new
> > FileReader("studentName.txt")));
> > String studentName = "not found";
> >
> > while ((studentName = reader.readLine()) != null) {
> > break;
> > }
> >
> > System.out.println(studentName);
> > }
> > }
> >
> > ```
> >
> > Because it automatically converts the `simplified try-with-resources
> > statement` to an equivalent `try-with-resources statement` after
> > compilation:
> >
> > ```java
> > public class example1_converted {
> > public static void main(String... args) throws
> > FileNotFoundException, IOException {
> > try (BufferedReader reader = new BufferedReader(new
> > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */
> > String studentName = "not found";
> >
> > while ((studentName = reader.readLine()) != null) {
> > break;
> > }
> >
> > System.out.println(studentName);
> > }/* Add '}' before the current scope ends. */
> > }
> > }
> >
> > ```
> >
> > Because the scope of `simplified try-with-resources statement` extends
> > until the current scope is terminated, the variable `studentName`,
> > which originally couldn't be resolved in `example1`, can now be
> > resolved successfully. This allows variable declaration, assignment,
> > and usage within the same scope, ensuring that resource close
> > operations occur before the current scope ends.
> >
> > Invoking multiple dependent `simplified try-with-resources statements`
> > is similar to regular variable declarations, thereby avoiding
> > excessive nesting.
> >
> > ```java
> > public class example3_simplified {
> > public static void main(String... args) throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password));
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > try (PreparedStatement statement =
> conn.prepareStatement(selectSQL));
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery());
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > System.out.println(studentName);
> > }
> > }
> >
> > ```
> >
> > It will be converted to equivalent `nested try-with-resources
> > statements` after compilation:
> >
> > ```java
> > public class example3_converted {
> > public static void main(String... args) throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password)) {/* Change ';' back to '{'. */
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > try (PreparedStatement statement =
> > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery()) {/* Change
> > ';' back to '{'. */
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > System.out.println(studentName);
> > }}}}/* Add the corresponding number (in this case, 3) of '}'
> > before the current scope ends. */
> > }
> >
> > ```
> >
> > After code formatting:
> >
> > ```java
> > public class example3_formatted {
> > public static void main(String... args) throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password)) {
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > try (PreparedStatement statement =
> > conn.prepareStatement(selectSQL)) {
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery()) {
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > System.out.println(studentName);
> > }
> > }
> > }
> > }
> > }
> >
> > ```
> >
> > Comparing the code structures of `example3_simplified` and
> > `example3_formatted`, it is evident that the usage of `simplified
> > try-with-resources statement` enhances code readability by
> > significantly reducing nested indentation levels. Additionally, it can
> > be observed that in the converted `nested try-with-resources
> > statements`, resource close actions will be executed in reverse order
> > of declaration, just before the current scope ends.
> >
> > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods
> > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to
> > create a simple, small-scale program:
> >
> > ```java
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > void main() throws SQLException {
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password));
> > try (PreparedStatement statement = conn.prepareStatement(selectSQL));
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery());
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > System.out.println(studentName);
> > }
> >
> > ```
> >
> >
> > ## Alternatives
> >
> > * One approach to mitigate the impact on readability caused by deeply
> > `nested try-with-resources statements` is to consolidate multiple
> > resources within the same `try-with-resources statement` as much as
> > possible. However, formatting multiple resources with line breaks can
> > also disrupt visual clarity, and furthermore, it's not always feasible
> > to close many resources simultaneously:
> >
> > ```java
> > public class alternative1 {
> > public static void main(String... args) throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password);
> > PreparedStatement statement =
> > conn.prepareStatement(selectSQL)) {
> > /* In this case, there are only 2 resources, but if there are 3
> or more,
> > it will become even less aesthetically pleasing. */
> >
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery()) {
> > /* ResultSet 'result' cannot be closed simultaneously with
> > PreparedStatement 'statement'
> > because PreparedStatement 'statement' needs parameter
> > configuration first. */
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > return studentName;
> > }
> > }
> > }
> > }
> >
> > ```
> >
> > * Alternatively, creating a separate method to confine the scope of
> > managed resources within the method can be done, but it necessitates
> > 'naming' the new
> > method[https://www.karlton.org/2017/12/naming-things-hard/]. This
> > approach simply shifts complexity to the interior of additional
> > method, which may not be conducive to creating simple, small-scale
> > programs:
> >
> > ```java
> > public class alternative2 {
> > public static void main(String... args) throws SQLException {
> > String selectSQL = "selectSQL";
> > String studentID = "studentID";
> > String studentName = getStudentName(selectSQL, studentID);
> >
> > System.out.println(studentName);
> > }
> >
> > public static String getStudentName(String sql, String studentID)
> > throws SQLException {
> > String jdbcUrl = "jdbcUrl";
> > String username = "username";
> > String password = "password";
> >
> > try (Connection conn = DriverManager.getConnection(jdbcUrl,
> > username, password)) {
> > try (PreparedStatement statement =
> conn.prepareStatement(sql)) {
> > statement.setString(1, studentID);
> >
> > try (ResultSet result = statement.executeQuery()) {
> > String studentName = "not found";
> >
> > while (result.next()) {
> > studentName = result.getString(1);
> > }
> >
> > return studentName;
> > }
> > }
> > }
> > }
> > }
> >
> > ```
> >
> > /* GET BETTER EVERY DAY */
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230925/20d46bc9/attachment-0001.htm>
More information about the amber-dev
mailing list