Не удается получить доступ к полю из статического контекста при передаче значения суперконструменту
У меня очень странная ошибка времени компиляции:
class Super {
Super(int[] array) {
}
}
class Sub extends Super {
private final int number = 1;
Sub() {
super(new int[] { number }); //error
}
}
Ошибка, которую я получаю,
Невозможно получить доступ к полю из статического контекста
Мой вопрос
Где статический контекст? Похоже, что статичность даже сыграет здесь роль.
Я наткнулся на эту попытку ответить на другой вопрос; нашел ошибку, которую я озадачил. Может ли кто-нибудь объяснить, где статический контекст?
Ответы
Ответ 1
От JLS 8.8.7
Явный оператор вызова конструктора в теле конструктора может не ссылаться на какие-либо переменные экземпляра или методы экземпляра или внутренние классы, объявленные в этом классе или любом суперклассе, или использовать this
или super
в любом выражении; в противном случае возникает ошибка времени компиляции.
Во время вызова super
экземпляр Sub
еще не существует
Ответ 2
Ваше поле number
должно быть статическим, чтобы вы могли использовать его в вызове конструктора. В противном случае вы получите cannot reference number before supertype constructor has been called
, потому что поле не доступно до вызова конструктора родительского класса.
Итак, ваш код должен выглядеть так:
class Super {
Super(int[] array) {
}
}
class Sub extends Super {
private static final int number = 1;
Sub() {
super(new int[] { number }); //error
}
}
Ответ 3
Дело в том, что super
должно быть вызвано до того, как что-либо еще будет сделано в подклассе. Это означает, что number
не инициализируется во время вызова, поэтому вы не можете передать его супер.
Что касается его "статического контекста", я не уверен.
Ответ 4
Попробуйте запустить следующую программу.
public class Main {
public static class Super
{
Super(int number) {
System.out.println("A");
}
}
public static class Sub extends Super {
private final Thing thing = new Thing();
Sub() {
super(3);
System.out.println("B");
}
}
public static final class Thing {
Thing() {
System.out.println("C");
}
}
public static void main(String[] args) {
new Sub();
}
}
Вывод: A, C, B. Сначала выполняется конструктор Super
, тогда поле экземпляра thing
of Sub
инициализируется. Наконец, выполняются строки конструктора Sub
, следующие за Super
.
Когда вы пытаетесь ссылаться на number
, поле number
еще не было инициализировано, и нет экземпляра Sub
. Сообщение об ошибке может быть более полезным. На IntelliJ я получаю "Не могу ссылаться на Sub.number до того, как был создан конструктор супертипа".
Ответ 5
Загрузка классов выполняется из дочернего класса (если он имеет основной метод или вызван другим классом для загрузки) в базовый класс, но инициализация происходит в обратном порядке (Базовый класс будет инициализирован первый). инициализация базового класса выполняется до этого, в то время ваше числовое поле не будет существовать, поскольку оно является переменной экземпляра.
Вы можете сделать его static (класс переменной) для загрузки ранее, а если его final, то компилятор сделает запись в пуле констант в время компиляции. Также JVM сможет получить его значение.