-
Bug
-
Resolution: Fixed
-
P3
-
22, 23
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
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