Не може да се направи справка с „X“, преди да е извикан конструктор на супертип

1. Общ преглед

В този кратък урок ще покажем как можем да получим грешката Не може да се направи препратка към „X“ преди да бъде извикан конструкторът на супертип и как да го избегнем.

2. Верига на конструкторите

Конструктор може да извика точно един друг конструктор. Този разговор трябва да е в първия ред на тялото му.

Можем да извикаме конструктор от същия клас с ключовата дума this или можем да извикаме конструктор на суперкласа с ключовата дума super .

Когато конструктор не извика друг конструктор, компилаторът добавя извикване към конструктора no-argument на суперкласа.

3. Нашата грешка при компилация

Тази грешка се свежда до опит за достъп до членове на ниво екземпляр, преди да извикаме веригата на конструктора.

Нека видим няколко начина, по които можем да се сблъскаме с това.

3.1. Позоваване на инстанционен метод

В следващия пример ще видим грешката при компилацията Не може да се препрати към „X“, преди да бъде извикан конструктор на супертип на ред 5. Имайте предвид, че конструкторът се опитва да използва метода на екземпляра getErrorCode () твърде рано:

public class MyException extends RuntimeException { private int errorCode = 0; public MyException(String message) { super(message + getErrorCode()); // compilation error } public int getErrorCode() { return errorCode; } } 

Тази грешка, тъй като u ntil super () е завършила , няма екземпляр на класа MyException . Следователно все още не можем да осъществим нашето извикване на метода на екземпляра getErrorCode () .

3.2. Позоваване на поле за екземпляр

В следващия пример виждаме нашето изключение с поле на екземпляр вместо метод на екземпляр. Нека да разгледаме как първият конструктор се опитва да използва член на екземпляр, преди самият екземпляр да е готов:

public class MyClass { private int myField1 = 10; private int myField2; public MyClass() { this(myField1); // compilation error } public MyClass(int i) { myField2 = i; } }

Препратка към поле на екземпляр може да се направи само след като неговият клас е инициализиран, което означава след всяко извикване на this () или super () .

И така, защо няма грешка в компилатора във втория конструктор, който също използва поле на екземпляр?

Не забравяйте, че всички класове са имплицитно извлечени от клас Object и затова има компилирано извикване super (), добавено от компилатора:

public MyClass(int i) { super(); // added by compiler myField2 = i; } 

Тук конструкторът на Object се извиква, преди да влезем в myField2 , което означава, че сме добре.

4. Решения

Първото възможно решение на този проблем е тривиално: не наричаме втория конструктор. Ние правим изрично в първия конструктор това, което искахме да направим във втория конструктор.

В този случай бихме копирали стойността на myField1 в myField2 :

public class MyClass { private int myField1 = 10; private int myField2; public MyClass() { myField2 = myField1; } public MyClass(int i) { myField2 = i; } } 

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

Но ако извикваме втория конструктор по основателна причина, например, за да избегнем повтаряне на код, можем да преместим кода в метод:

public class MyClass { private int myField1 = 10; private int myField2; public MyClass() { setupMyFields(myField1); } public MyClass(int i) { setupMyFields(i); } private void setupMyFields(int i) { myField2 = i; } } 

Отново, това работи, защото компилаторът имплицитно е извикал веригата на конструктора, преди да извика метода.

Трето решение може да бъде, че използваме статични полета или методи . Ако променим myField1 на статична константа, тогава компилаторът също е доволен:

public class MyClass { private static final int SOME_CONSTANT = 10; private int myField2; public MyClass() { this(SOME_CONSTANT); } public MyClass(int i) { myField2 = i; } } 

Трябва да отбележим, че създаването на статично поле означава, че то става споделено с всички екземпляри на този обект, така че не е промяна, която да правите прекалено леко.

За да бъде правилният отговор статичният , имаме нужда от сериозна причина. Например, може би стойността всъщност не е поле, а вместо това е константа, така че има смисъл тя да бъде статична и окончателна . Може би конструктивният метод, който искахме да извикаме, не се нуждае от достъп до членовете на екземпляра на класа, което означава, че трябва да е статичен .

5. Заключение

Видяхме в тази статия как правенето на препратка към членове на екземпляра преди извикването super () или this () дава грешка при компилацията. Видяхме това да се случи с изрично деклариран базов клас, а също и с неявния базов клас Object .

Също така демонстрирахме, че това е проблем с дизайна на конструктора и показахме как това може да бъде коригирано чрез повтаряне на код в конструктора, делегиране на метод за настройка след изграждане или използване на константни стойности или статични методи в помощ при изграждането .

Както винаги изходният код за този пример може да бъде намерен в GitHub.