Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8329595

spurious variable "might not have been initialized" on static final field

XMLWordPrintable

    • 22
    • b19
    • generic
    • generic

      ADDITIONAL SYSTEM INFORMATION :
      openjdk 22 2024-03-19
      OpenJDK Runtime Environment (build 22+36-2370)
      OpenJDK 64-Bit Server VM (build 22+36-2370, mixed mode, sharing)

      openjdk 23-ea 2024-09-17
      OpenJDK Runtime Environment (build 23-ea+16-1297)
      OpenJDK 64-Bit Server VM (build 23-ea+16-1297, mixed mode, sharing)

      Linux 6.8.2-arch2-1 #1 SMP PREEMPT_DYNAMIC Thu, 28 Mar 2024 17:06:35 +0000 x86_64 GNU/Linux

      A DESCRIPTION OF THE PROBLEM :
      javac App.java (and java App.java) report a spurious variable "might not have been initialized" for static final field of a non-static inner class if the field is initialized inside a try-catch.
      OpenJDK 17 and 21 accepted this initialization pattern for static fields of non-static inner classes. Javac in JDK 22.0.2 and 23 (build ea+16). In 22 and 23, this pattern only works for static inner classes or top-level classes.

      The use case for initializing inside a try-catch is initializing VarHandles to fields. The JDK itself uses this pattern. One example of such usage in the JDK is java.lang.ThreadBuilders.BaseThreadFactory.COUNT, although BaseThreadFactory is a static inner class.

      This behavior change is not exclusive to VarHandles. Initializing a static final String with a string literal will also trigger the behavior change if the assignment is inside a try-catch.

      REGRESSION : Last worked in version 21.0.2

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create a App.java file with the following content:

      ```
      public class App {
          public static void main( String[] args ) {
              System.out.println("Hi"+new App().inner().name());
          }

          private Inner inner() { return new Inner(); }

          public final class Inner {
              public static final String NAME;
              static {
                  try {
                      NAME = "bob";
                  } catch (Exception e) { throw new ExceptionInInitializerError(e); }
              }

              public String name() {return NAME;}
          }
      }
      ```

      Compile with javac App.java or directly execute with java App.java.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      App.java gets compiled without error
      ACTUAL -
      App.java:9: error: variable NAME might not have been initialized
              public static final String NAME;
                                         ^
      1 error

      ---------- BEGIN SOURCE ----------
      public class App {
          public static void main( String[] args ) {
              System.out.println("Hi"+new App().inner().name());
          }

          private Inner inner() { return new Inner(); }

          public final class Inner { // note: not static
              public static final String NAME; // use case is VarHandle, but will fail with String
              static {
                  try {
                      NAME = "bob"; // use case is MethodHandles.lookup().findVarHandle(), which throws checked exceptions
                  } catch (Exception e) { throw new ExceptionInInitializerError(e); }
              }

              public String name() {return NAME;}
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Revert to JDK 21 or change the source code with one of the following approaches:
      1. move the static fields of the inner class to the containing class
      2. make the inner class a top-level class
      3. Use sun.misc.Unsafe if the above changes, which affect visibility, are not possible

      FREQUENCY : always


            acobbs Archie Cobbs
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: