Странный ложноположительный анализ потока данных javac - PullRequest
3 голосов
/ 02 февраля 2012

У меня есть код следующей формы:

class Test {
  private final A t;

  public Test() {

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

    t = new A();
  }

  private class A {}
}

Компилятор говорит:

variable t might already have been assigned

Интересно, если я выполню любое из следующих изменений в цикле, это сработает!

  • Изменить содержимое цикла на A u = null
  • Удалить цикл (но сохранить final A u = null;)
  • Заменить цикл в стиле foreach на классический цикл подсчета

Что здесь происходит?

Примечание: Я не смог получить минимальный пример, вызывающий ошибку, поэтому, вероятно, что-то не так с "средой""(около 1400 мест).Я не вижу, что могло бы помешать инициализации t, так как t записывается в никуда.

Забавный факт: IntelliJ IDEA говорит, что переменная 'u' может иметь модификатор 'final'... "если я его удалю.

Я использую javac 1.6.0_26.

Обновление: Вот, пожалуйста, этот пример, так что так минимальный:

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;
    }
  }
}

Не компилируется на javac 1.6.0_26, но компилируется на javac 1.7.0_02.Итак, я думаю, что я попал в какой-то злой угловой ящик ... чего-то?

Обратите внимание, что вы можете сделать любой из

  • Удалить любого одного члена
  • Удалить final внутри цикла в A()
  • Замените цикл на обычный цикл for, например, for ( int i=0; i<100; i++ ) { ... }

, и он будет скомпилирован.

Ответы [ 4 ]

1 голос
/ 06 февраля 2012

Поскольку проблема исправлена ​​в Java 7, это, вероятно, ошибка в компиляторе Java 6.

1 голос
/ 02 февраля 2012

Если ваш конструктор вызвал другой конструктор, который сам не установил t, компилятор не поймет этого.

См. здесь .

1 голос
/ 02 февраля 2012

Если у вас много кода, я бы попробовал это.

private final A t;

public Test() {
    final int t = 1;

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

   this.t = new A();

Это приведет к сбою любого кода, который может "инициализировать" t (и появится в компиляторе.

0 голосов
/ 02 февраля 2012

Насколько я понимаю, сохранение объекта в окончательной переменной не делает ваш объект неизменным, а ссылается на него.Это объясняет, почему, когда вы удаляете последнее ключевое слово, которое оно работает, и в соответствии с удалением цикла for, я думаю, что вы обращаетесь к ссылке на объект, а не к экземпляру.

...