>
>
>
V6050. Class initialization cycle is pr…


V6050. Class initialization cycle is present.

Данное диагностическое правило выявляет случаи неправильного порядка объявления статических полей внутри класса, а также случаи зависимостей статических полей от других классов. Такие случаи могут привести к сложному поддержанию кода или к ошибочной инициализации класса.

Случай 1.

Использование статического поля до того, как оно будет проинициализировано.

Рассмотрим синтетический пример некорректного кода:

public class Purse
{
  static private Purse reserve = new Purse(10);
  static private int scale = 5 + (int) (Math.random() * 5);

  private int deposit;

  Purse() {
    deposit = 0;
  }
  
  Purse(int initial) {
    deposit = initial * scale;
  }
  ...
}

Как известно, статические поля будут инициализироваться в порядке объявления самыми первыми при первом использовании класса. Следовательно, в данном примере сначала инициализируется 'reserve', а потом 'scale'.

Рассмотрим инициализацию статического поля 'reserve':

  • Вызывается конструктор 'Purse' с аргументом 'initial = 10'.
  • На момент выполнения выражения 'initial * scale' в вызываемом конструкторе, поле 'scale' еще не инициализировано, и будет равно значению по умолчанию (0), вместо значения из диапазона [5;10].

Вследствие чего, поле 'deposit' у объекта 'reserve' будет проинициализировано не так, как задумывалось.

Для того, чтобы исправить ситуацию, необходимо поменять порядок объявления статических полей:

public class Purse
{
  static private int scale = 5 + (int) (Math.random() * 5);
  static private Purse reserve = new Purse(10);
  
  private int deposit;

  Purse() {
    deposit = 0;
  }
  
  Purse(int initial) {
    deposit = initial * scale;
  }
  ...
}

Случай 2.

Взаимная зависимость статических полей разных классов.

Рассмотрим синтетический пример некорректного кода:

public class A {
  public static int scheduleWeeks = B.scheduleDays / 7 + 1;
  ....
}
....
public class B {
  public static int scheduleDays = A.scheduleWeeks * 7 + 7;
  ....
}

Ситуация здесь такова, что статическое поле 'A.scheduleWeeks' зависит от статического поля 'B.scheduleDays', и наоборот. Порядок инициализации классов может меняться, отсюда меняется и порядок инициализации статических полей. Если инициализируется первым класс 'A', то 'A.scheduleWeeks' будет иметь значение 2, а 'B.scheduleDays' – 7. Если первым будет класс 'B', то 'A.scheduleWeeks' будет равен 1, а 'B.scheduleDays' – 14. Вряд ли такое поведение будет приятно наблюдать у себя в коде. В таком случае необходимо пересмотреть инициализацию этих полей и убрать зависимость друг от друга.

Например, можно одно из статических полей инициализировать константой, и эта зависимость исчезнет:

public class A {
  public static int scheduleWeeks = B.scheduleDays / 7 + 1;
  ....
}
....
public class B {
  public static int scheduleDays = 14;
  ....
}

В таком случае 'B.scheduleDays' всегда будет равен 14, а 'A.scheduleWeeks' – 3.

Случай 3.

Инициализация статического поля в одном классе, используя статический метод второго класса, который в свою очередь использует статический метод или поле первого класса.

Рассмотрим синтетический пример некорректного кода:

public class A {
  public static int scheduleWeeks = B.getScheduleWeeks();
  public static int getScheduleDays() { return 21; }
  ....
}
....
public class B {
  public static int getScheduleWeeks() {return A.getScheduleDays()/7;}
  ....
}

Независимо от того, какой из классов первым будет инициализирован, поле 'A.scheduleWeeks' примет значение равное 3. Несмотря на это, такой способ инициализации сбивает с толку при чтении и поддержке такого кода.

Например, можно подкорректировать код следующим образом:

public class A {
  public static int scheduleWeeks = B.getScheduleWeeks();
  
  ....
}
....
public class B {
  public static int getScheduleDays() { return 21; }
  public static int getScheduleWeeks() {return B.getScheduleDays()/7;}
}

Данная диагностика классифицируется как:

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V6050.