Weird false-positive of javac data flow analysis

2019-05-15 06:59发布

问题:

I have code of the following form:

class Test {
  private final A t;

  public Test() {

    for ( ... : ... ) {
      final A u = null;
    }

    t = new A();
  }

  private class A {}
}

Compiler says:

variable t might already have been assigned

Interestingly, if I perform any of the following changes to the loop it works out!

  • Change the loop's content to A u = null
  • Remove the loop (but keep final A u = null;)
  • Replace the foreach-style loop with a classic counting loop

What is going on here?

Note: I could not get the minimal example to cause the error so there is probably something wrong with the "environment" (about 1400 loc). I can not see what could disturb the initialisation of t, though, as t is written to nowhere else.

Fun fact: IntelliJ IDEA says "Variable 'u' can have 'final' modifier..." if I remove it.

I use javac 1.6.0_26.

Update: There you go, this example so so minimal:

import java.util.List;

class A {
  private final boolean a;

  public A() {
    for ( final Object o : new Object[] {} ) {
      final Object sh = null;
    }

    a = true;
  }

  class B {
    private final Object b1;
    private final Object b2;

    B() {
      b1 = null;
      b2 = null;
    }
  }
}

Fails to compile on javac 1.6.0_26 but compiles on javac 1.7.0_02. So I guess I hit some wicked corner case of ... something?

Note that you can do any of

  • Remove any one member
  • Remove final inside the loop in A()
  • Replace the loop with a normal for loop, e.g. for ( int i=0; i<100; i++ ) { ... }

and it will compile.

回答1:

If you have lots of code I would try this.

private final A t;

public Test() {
    final int t = 1;

   for ( ... ) {
      final A u = null;
   }

   this.t = new A();

This will cause any code which "might" initialise t to fail (and show up in the compiler.



回答2:

If you're constructor happen to call another constructor that doesn't itself set t, the compiler fails to understand that.

See here.



回答3:

As the problem is fixed in Java 7, it is probably a bug in the Java 6 compiler.



回答4:

It is my understanding that storing an object in a final var does not make your object immutable but its reference. That explain why when you remove the final keyword it works and as per removing the for-loop, i think you are accessing the object reference and not an instance.