JDK-8289777 : javac does not see a problem when Eclipse emits a 'blank final field may not have been initialized' error
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2022-07-05
  • Updated: 2022-08-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 20
20Unresolved
Related Reports
Relates :  
Relates :  
Relates :  
Description
This simple code generates an error in Eclipse (Version: 2022-06 (4.24.0)), but not in javac (see related issue JDK-8289171 in javafx).

-----
package goryachev.bugs;

import java.lang.reflect.Field;

public class TestNullFinal {
    private
    // uncomment to cause a 'blank final field may not have been initialized' error in Eclipse
    //final 
    A isNull;
    
    public TestNullFinal() {
        isNull = new A("constructor");
        System.out.println("test=" + test);
    }
    
    public static void main(String[] args) {
        new TestNullFinal();
    }
    
    private Runnable runnable = () -> {
        // ERROR: blank final field may not have been initialized
        isNull.hashCode();
    };
    
    private Object test = isNullValue();
    
    protected Object isNullValue() {
        return (isNull);
    }
    
    protected static class A {
        public A(String text) {
            System.out.println(text);
        }
    }
}

-----

To reproduce, uncomment line 8 (// final).

The problem is further illustrated by running the test case as is, notice that the variable isNull when the 'test' field is initialized:

stderr output:
constructor
test=null
Comments
To my understanding of chapter 16, the following test code is not accepted; correctly. Nobody argues with that. public class Access { final Object o; Runnable runnable = () -> this.o.toString(); { o = 42; } } It is not accepted because `this.o` is considered an access and because it is not definitely assigned before the expression or block that is the lambda body. According to the spec `this.o` is considered an access because it is qualified by this. However, when the access is a qualified `this` (15.8.4. Qualified this), it is not an access i.e., `Access.this.o`. This is the reason it is accepted and that the spec and javac are in sync. To me, it seems that leaving the specially permitted case of Qualified This was intentional.
08-08-2022

This is the bug in the Eclipse tracker regarding this issue: https://bugs.eclipse.org/bugs/show_bug.cgi?id=507629 The test case there is: public class Test { final Object o; Runnable runnable = () -> Test.this.o.toString(); // error here public Test() { o = new Object(); } } They also linked to this JBS issue: https://bugs.openjdk.org/browse/JDK-8043176 Eclipse treats this as a bug in the sense that their compiler does not accept the code, but javac does. If it is determined that javac should not accept the code, they might change their mind. I don't know what their interpretation of the assignment rules are. It might be helpful to invite them into the discussion if one arises.
15-07-2022

Sorry, I was not able to find a version of javac that would not produce an error for the provided testcase (with 'final' uncommented): /usr/lib/jvm/java-8-openjdk-amd64/bin/javac /tmp/TestNullFinal.java /tmp/TestNullFinal.java:22: error: variable isNull might not have been initialized isNull.hashCode(); ^ 1 error I looked at the JFX bug, and it seems the actual testcase is more likely to be along these lines: --- public class TestNullFinal2 { private final Object isNull; public TestNullFinal2() { isNull = ""; } private Object o = new Object() { Runnable r = () -> { TestNullFinal2.this.isNull.hashCode(); }; }; } --- javac accepts this program. I am not quite clear this is a bug in javac. Looking at the JLS: https://docs.oracle.com/javase/specs/jls/se17/html/jls-16.html There is: For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs. Where access is defined above as: An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (ยง15.26.1). Here, the simple name is qualified by "TestNullFinal2.this", not by (specifically) "this", so it is not quite clear to that this is considered to be an access as per the DU/DA definition.
15-07-2022