简体   繁体   English

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

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

One of my classes inherits from a class in a framework I use. 我的一个类继承自我使用的框架中的类。 The superclass calls a method in its constructor which I overwrite in my own class. 超类在其构造函数中调用一个方法,我在自己的类中覆盖它。 The method uses a field I want to initialize before it is called by the super constructor to avoid a NullPointerException. 该方法使用我想要在超级构造函数调用之前初始化的字段以避免NullPointerException。

Is there any way to do this? 有没有办法做到这一点?

Here is a synthetic test scenario, I want c in Child to not be null when call is called. 这是一个综合测试场景,我希望在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();
    }
}

Prior to Java 7, that was possible. 在Java 7之前,这是可能的。 I could get by with stunts like this: 我可以得到这样的特技:

    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"));
        }
    }

Now, that won't work anymore. 现在,这将不再适用。 I appreciate the additional safety, but the call from super destroys any safety gained by it and reduces the flexibility. 我很欣赏额外的安全性,但是超级电话会破坏它所带来的任何安全性并降低灵活性。

I'd like a way to circumvent this restriction. 我想要一种规避这种限制的方法。 As an alternative, I'd like to know what's gained by an restriction that spares the super constructor case. 作为替代方案,我想知道什么是通过限制来获得超级构造函数的情况。

A static initializer will be called before the super class constructor. 将在超类构造函数之前调用静态初始值设定项。 However, you won't be able to set any non-static fields, so it most likely won't help. 但是,您将无法设置任何非静态字段,因此很可能无济于事。

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

A non-static initialization block also doesn't help as it is called after the super class constructor completes. 非超级构造函数完成后调用非静态初始化块也没有帮助。

Another approach may be to do nothing when called from the super-constructor and make the call again the child constructor, eg: 另一种方法可能是在从超级构造函数调用时不执行任何操作,并再次调用子构造函数,例如:

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

    public void call() {

       if (c==null) {
         return;
       }

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

    }

This won't work if more stuff happens in the super constructor that is dependent on this method though. 如果在依赖于此方法的超级构造函数中发生更多内容,这将不起作用。

I have to agree with EJP that this is all a bad idea; 我不得不同意EJP的观点,这是一个坏主意; it would be much better to find a completely different solution that doesn't involve torturing constructors. 找到一个完全不同的解决方案,不涉及折磨构造函数会好得多。

Note that your class Child is translated into the following equivalent by the Java compiler: 请注意,Java编译器将您的类Child转换为以下等效项:

public static class Child extends Parent {

    private Child c;

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

    // Remaining implementation
}

This is the same for Java 6 and 7, the generated byte code for the constructor is even the same when compiling with any of both versions. 对于Java 6和7,这是相同的,当使用任何两个版本进行编译时,构造函数的生成字节代码甚至是相同的。 The local field is always instantiated after calling the super constructor. 在调用超级构造函数之后,始终会实例化本地字段。 What compiler did you use to make your "work-around" work? 您使用什么编译器来使您的“解决方案”工作?

This restriction is quite elementary. 这种限制是非常基本的。 This way, you can rely on that super constructors are applied first. 这样,您可以依赖首先应用的超级构造函数。 Imagine, your sub constructor was using a final field declared in this class. 想象一下,你的子构造函数正在使用在这个类中声明的final字段。 You could not guarantee that this field was initialized if you would not guarantee this constructor execution order. 如果您不保证此构造函数执行顺序,则无法保证此字段已初始化。 This restriction makes Java more reliable. 这种限制使Java更可靠。

This is an answer to the "I'd like to know what's gained by an restriction that spares the super constructor case." 这是对“我想知道通过超级构造函数案例的限制所获得的内容”的答案。 part of the question. 部分问题。

In the course of construction, there are three states the fields declared in a class X might be in: All default values, all fully initialized to consistent working values, and anything else. 在构造过程中,有三种状态,在类X中声明的字段可能在:所有默认值,全部初始化为一致的工作值,以及其他任何内容。

The objective seems to be that code in classes other than X only sees one of the first two states. 目标似乎是除了X之外的类中的代码只能看到前两个状态中的一个。 When non-static initializer or constructor code for any of X's superclasses is running, X's fields are all in the default state. 当任何X超类的非静态初始化程序或构造函数代码正在运行时,X的字段都处于默认状态。 When non-static initializer or constructor code for any subclass of X is running, all X's fields have been initialized to a fully consistent, usable state. 当X的任何子类的非静态初始化程序或构造函数代码正在运行时,所有X的字段都已初始化为完全一致的可用状态。

Only X initializer and constructor code should have to deal with X fields in an inconsistent state, some initialized, some default, and some partially initialized. 只有X初始化程序和构造函数代码必须处理处于不一致状态的X字段,一些是初始化的,一些是默认的,一些是部分初始化的。

This can certainly be circumvented by calling X methods from an X superclass initializer or constructor, but that is commonly regarded as an anti-pattern. 通过从X超类初始化器或构造函数调用X方法可以避免这种情况,但这通常被视为反模式。 The problem is running X code that is not called locally from an initializer or constructor in a partially constructed X. If that code changes a field, the change may be overwritten when the X initializers and constructor body run. 问题是运行的X代码不是从部分构造的X中的初始化程序或构造函数本地调用的。如果该代码更改字段,则在X初始化程序和构造函数体运行时可能会覆盖此更改。

This should never have worked in the first place. 这应该永远不会起作用。

Note that at the bytecode level, this actually is allowed. 请注意,在字节码级别,实际上允许这样做。 In bytecode, you can set fields declared in the current class before calling a superclass constructor. 在字节码中,您可以在调用超类构造函数之前设置当前类中声明的字段。 However, Java provides no way to use this behavior. 但是,Java无法使用此行为。 It is only used secretly by the Java compiler to initialize the synthetic fields added to support inner classes. 它仅由Java编译器秘密使用,以初始化为支持内部类而添加的合成字段。

暂无
暂无

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

相关问题 为什么子类必须在超类中调用no args构造函数? - why does the subclass have to invoke the no args constructor in the super class? 如果超类没有一个默认构造函数,为什么此Java代码中的子类具有默认构造函数? - Why does the subclass in this Java code have a default constructor if the super-class does not have one? 在线程1中调用的构造方法,仅在线程2中访问的字段-是否需要volatile? - Constructor called in Thread 1, fields accessed exclusively in Thread 2 - volatile needed? 为什么 this() 和 super() 必须是构造函数中的第一条语句? - Why do this() and super() have to be the first statement in a constructor? 为什么 super() 不会在另一个构造函数中被调用? - Why does super() not get called in the other constuctor? 从构造函数中调用时,关键字“ super”如何工作? - How does the keyword “super” work, when called from a constructor? java抽象类,构造器未在超类中调用,为什么? - java abstract class, Constructor not called in super-class, Why? 为什么必须使用 super() 而不是类名调用超类构造函数? - Why superclass constructor must be called with super() and not with its class name? 为什么在设置之前调用构造函数方法 - Why is the constructor method being called before setup 为什么不能从枚举构造函数中调用超级构造函数? - Why can't the super constructor be invoked from an enum constructor?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM