#java #inheritance #sonarqube
#java #наследование #sonarqube
Вопрос:
Я запускал модульные тесты с помощью Sonarqube для некоторого кода Java, и одна из проблем, которые я обнаружил, заключается в следующем.
Классы не должны обращаться к своим собственным подклассам во время инициализации
Когда родительский класс ссылается на член подкласса во время своей собственной инициализации, результаты могут быть не такими, как вы ожидаете, потому что дочерний класс, возможно, еще не был инициализирован. Это может привести к так называемому «циклу инициализации» или даже к тупиковой ситуации в некоторых крайних случаях.
Это пример кода, который Sonarqube использовал для описания проблемы:
class Parent {
static int field1 = Child.method(); // Noncompliant
static int field2 = 42;
public static void main(String[] args) {
System.out.println(Parent.field1); // will display "0" instead of "42"
}
}
class Child extends Parent {
static int method() {
return Parent.field2;
}
}
Далее следует упрощение кода, из-за которого возникла проблема.
abstract class Parent {
static Parent childInstance = new Child();
}
Я действительно не понимаю, почему это проблема. В примере Sonarqube родительский класс инициализируется field1
путем вызова статического метода дочернего класса. Это означает, что мне не нужно создавать экземпляр дочернего класса перед вызовом метода.
Во втором фрагменте кода родительский класс пытается создать экземпляр дочернего класса, а не вызывать один из его методов.
Не могли бы вы объяснить мне, почему невозможно ссылаться на дочерний элемент в родительском классе?
РЕДАКТИРОВАТЬ: я использую SonarScanner для Maven, и моя версия Sonarqube — 8.4.1.35646. Идентификатор правила проблемы — S2390.
Комментарии:
1. Какая версия sonarqube? Кроме того, вы можете поделиться идентификатором правила?
Ответ №1:
Подумайте о том, когда здесь выполняется код:
abstract class Parent {
static Parent childInstance = new Child();
}
Поскольку ваше присвоение static
полю new Child()
должно быть выполнено задолго до создания первого Parent
экземпляра.
А именно во время инициализации класса (для простоты предположим, что это эквивалентно «загрузке класса», хотя это не совсем точно).
Это означает, что во время инициализации класса Parent
он создает новый экземпляр Child
. Обычно первое, что должно произойти для завершения инициализации, — это то, что суперкласс уже инициализирован. Поскольку мы в настоящее время инициализируем Parent
, мы можем знать, что Child
он еще не может быть полностью инициализирован (по определению, поскольку для этого потребуется Parent
полная инициализация).
Есть некоторые «трюки», которые использует спецификация языка JVM / Java, чтобы фактически позволить этой конструкции работать, но у них есть недостатки (например, возможность наблюдать неинициализированные final
поля).
Подумайте об этом так: вы пытаетесь захватить что-то со второго этажа дома, пока вы заняты строительством подвала.