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. |