繁体   English   中英

为什么在访问字段之前必须调用超级构造函数?

[英]Why does the super constructor have to be called before fields can be accessed?

我的一个类继承自我使用的框架中的类。 超类在其构造函数中调用一个方法,我在自己的类中覆盖它。 该方法使用我想要在超级构造函数调用之前初始化的字段以避免NullPointerException。

有没有办法做到这一点?

这是一个综合测试场景,我希望在call时, Child c不为null。

public class Test {

    public static class Parent {
        public Parent() {
            super();
            call();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Parent");
        }
    }

    public static class Child extends Parent {

        private Child c = this;

        public Child() {
            super();
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

    public static void main(String[] args) {
        new Child();
    }
}

在Java 7之前,这是可能的。 我可以得到这样的特技:

    public static class Child extends Parent {

        private Child c;

        private Child(Object unused) {
            super();
        }

        public Child() {
            this(c = this);
        }

        // only called from parent constructor
        public void call() {
            System.out.println("Child, c is " + (c == null ? "null" : "this"));
        }
    }

现在,这将不再适用。 我很欣赏额外的安全性,但是超级电话会破坏它所带来的任何安全性并降低灵活性。

我想要一种规避这种限制的方法。 作为替代方案,我想知道什么是通过限制来获得超级构造函数的情况。

将在超类构造函数之前调用静态初始值设定项。 但是,您将无法设置任何非静态字段,因此很可能无济于事。

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

非超级构造函数完成后调用非静态初始化块也没有帮助。

另一种方法可能是在从超级构造函数调用时不执行任何操作,并再次调用子构造函数,例如:

    public Child() {
        super();
        call();
    }

    public void call() {

       if (c==null) {
         return;
       }

       System.out.println("do something with c now");

    }

如果在依赖于此方法的超级构造函数中发生更多内容,这将不起作用。

我不得不同意EJP的观点,这是一个坏主意; 找到一个完全不同的解决方案,不涉及折磨构造函数会好得多。

请注意,Java编译器将您的类Child转换为以下等效项:

public static class Child extends Parent {

    private Child c;

    public Child() {
        super();
        c = this;
    }

    // Remaining implementation
}

对于Java 6和7,这是相同的,当使用任何两个版本进行编译时,构造函数的生成字节代码甚至是相同的。 在调用超级构造函数之后,始终会实例化本地字段。 您使用什么编译器来使您的“解决方案”工作?

这种限制是非常基本的。 这样,您可以依赖首先应用的超级构造函数。 想象一下,你的子构造函数正在使用在这个类中声明的final字段。 如果您不保证此构造函数执行顺序,则无法保证此字段已初始化。 这种限制使Java更可靠。

这是对“我想知道通过超级构造函数案例的限制所获得的内容”的答案。 部分问题。

在构造过程中,有三种状态,在类X中声明的字段可能在:所有默认值,全部初始化为一致的工作值,以及其他任何内容。

目标似乎是除了X之外的类中的代码只能看到前两个状态中的一个。 当任何X超类的非静态初始化程序或构造函数代码正在运行时,X的字段都处于默认状态。 当X的任何子类的非静态初始化程序或构造函数代码正在运行时,所有X的字段都已初始化为完全一致的可用状态。

只有X初始化程序和构造函数代码必须处理处于不一致状态的X字段,一些是初始化的,一些是默认的,一些是部分初始化的。

通过从X超类初始化器或构造函数调用X方法可以避免这种情况,但这通常被视为反模式。 问题是运行的X代码不是从部分构造的X中的初始化程序或构造函数本地调用的。如果该代码更改字段,则在X初始化程序和构造函数体运行时可能会覆盖此更改。

这应该永远不会起作用。

请注意,在字节码级别,实际上允许这样做。 在字节码中,您可以在调用超类构造函数之前设置当前类中声明的字段。 但是,Java无法使用此行为。 它仅由Java编译器秘密使用,以初始化为支持内部类而添加的合成字段。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM