【问题标题】:Cannot access field from static context when passing value to superconstructor将值传递给超级构造函数时无法从静态上下文访问字段
【发布时间】:2015-03-01 01:56:05
【问题描述】:

我遇到了一个非常奇怪的编译时错误:

class Super {
    Super(int[] array) {

    }
}

class Sub extends Super {
    private final int number = 1;

    Sub() {
        super(new int[] { number }); //error
    }
}

我得到的错误是

无法从静态上下文访问字段

我的问题

静态上下文在哪里?似乎静态甚至不会在这里发挥作用。

我偶然发现了这个试图回答别人的问题;发现我莫名其妙的错误。有人能解释一下静态上下文在哪里吗?

【问题讨论】:

  • 您使用的是哪个编译器?我得到cannot reference number before supertype constructor has been called。另见 JLS 8.8.7.1。 Explicit Constructor Invocations 声明:“构造函数主体中的显式构造函数调用语句不能引用在此类或任何超类中声明的任何实例变量或实例方法或内部类,或使用 this或 super 任何表达式;否则,会发生编译时错误。”
  • @ZouZou 我第一次遇到它是在我手机的 ide AIDE 上,我很确定它使用的是 Dalvik 编译器。然后我跳上我的电脑在 Eclipse Luna 上尝试它,使用他们的 JDK 8u25 编译器并得到相同的错误消息。我不确定为什么它会给我静态上下文错误,所以我认为我应该重新安装 Eclipse。至于 AIDE,我对它给了我一个时髦的错误并不感到惊讶

标签: java inheritance static


【解决方案1】:

您的字段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
    }
}

【讨论】:

  • 这不是我收到的错误,而是应该显示的正确错误。这就是问题所在。那是几年前的事了,但回头看,这似乎是最好的答案。感谢您的投稿,抱歉这么久才接受。尽管其他答案提到了 JLS 查询,但它并不表示通知错误,也没有指定 proper 通知是什么。
【解决方案2】:

来自JLS 8.8.7

构造函数体中的显式构造函数调用语句不能引用在该类或任何超类中声明的任何实例变量或实例方法或内部类,或在任何表达式中使用thissuper;否则,会发生编译时错误。

super 调用时,Sub 的实例尚不存在

【讨论】:

  • + 1. 我认为你的最后一句话是重点。由于那里没有可用的实例,因此编译器将其视为“静态上下文”实际上是有意义的,就像在静态方法、静态初始化块或静态字段的初始化表达式中一样。 (就个人而言,我认为编译器最好为这个上下文使用单独的错误消息,只是为了清晰和避免混淆。但约束与普通静态上下文中的相同,所以编译器编写者可能太受诱惑了不要重复自己。)
【解决方案3】:

问题是,super 必须在子类中执行任何其他操作之前调用。这意味着,number 在调用时没有初始化,所以你不能将它传递给 super。 至于它与“静态上下文”有什么关系,我不确定。

【讨论】:

    【解决方案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的构造函数,然后初始化Sub的实例字段thing。最后,super 之后的 Sub 构造函数行将被执行。

    当您尝试引用number 时,number 字段尚未初始化并且没有Sub 实例。错误消息可能更有帮助。在 IntelliJ 上,我得到“在调用超类型构造函数之前无法引用 Sub.number”。

    【讨论】:

      【解决方案5】:

      类的加载是从子类(如果它具有main方法或由其他类加载)到基类完成的,但是初始化以相反的顺序发生(基类将被初始化首先)。基类初始化之前完成,那时你的数字字段将不存在,因为它是实例变量。

      您可以使其 static(class variable) 提前加载,如果它的 final 则编译器将在 constant pool 中创建一个条目仅编译时间。这样 JVM 才能获得它的值。

      【讨论】:

        猜你喜欢
        • 2020-04-02
        • 1970-01-01
        • 1970-01-01
        • 2019-03-18
        • 2022-08-14
        • 1970-01-01
        • 1970-01-01
        • 2019-04-25
        • 1970-01-01
        相关资源
        最近更新 更多