Idea[Simplified try-with-resources statement]
Tagir Valeev
amaembo at gmail.com
Mon Sep 25 07:22:30 UTC 2023
Hello!
Looking at 'vanilla Java' sample, I assume that there _was_ demand before
Java 7. Now, it's basically covered with try-with-resources in a more
explicit and robust way.
This cannot serve as good statistical data, but I monitor support requests
in IntelliJ IDEA, regarding Lombok. Due to the hacky nature of Lombok, we
have many problems with it, and people report that some inspections don't
play well with certain Lombok annotations. So far, I don't remember a
single report concerning Cleanup annotation, which means that either it's
perfectly supported (I doubt), or nobody uses it.
Also, I should note that we are in the wrong mailing list. This list
discusses the implementation of new language features, not their design.
You probably need amber-spec-comments.
With best regards,
Tagir Valeev.
On Mon, Sep 25, 2023, 09:13 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/8c5e0911/attachment-0001.htm>
More information about the amber-dev
mailing list