super用法 - super java是什么意思




为什么this()和super()必须是构造函数中的第一条语句? (12)

所以,它不会阻止你在调用super之前执行逻辑。 它只是阻止你执行不适合单个表达式的逻辑。

实际上,你可以执行几次执行的逻辑,你只需要将你的代码封装在一个静态函数中,并在超级语句中调用它。

使用你的例子:

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}

Java要求如果在构造函数中调用this()或super(),它必须是第一个语句。 为什么?

例如:

public class MyClass {
    public MyClass(int x) {}
}

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
    }
}

Sun编译器说“调用super必须是构造函数中的第一条语句”。 Eclipse编译器说“构造函数调用必须是构造函数中的第一条语句”。

但是,您可以通过稍微重新安排代码来解决此问题:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        super(a + b);  // OK
    }
}

这是另一个例子:

public class MyClass {
    public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
    public MySubClassA(Object item) {
        // Create a list that contains the item, and pass the list to super
        List list = new ArrayList();
        list.add(item);
        super(list);  // COMPILE ERROR
    }
}

public class MySubClassB extends MyClass {
    public MySubClassB(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(Arrays.asList(new Object[] { item }));  // OK
    }
}

所以,它不会阻止你在调用super之前执行逻辑 。 它只是阻止你执行不适合单个表达式的逻辑。

调用this()有类似的规则。 编译器说“调用它必须是构造函数中的第一条语句”。

为什么编译器有这些限制? 你可以举一个代码示例,如果编译器没有这个限制,会发生什么坏事?


构造函数按推导顺序完成执行是有意义的。 因为超类没有任何子类的知识,所以它需要执行的任何初始化与子类执行的任何初始化都是分开的,并且可能是先决条件。 因此,它必须首先完成它的执行。

一个简单的演示:

class A {
    A() {
        System.out.println("Inside A's constructor.");
    }
}

class B extends A {
    B() {
        System.out.println("Inside B's constructor.");
    }
}

class C extends B {
    C() {
        System.out.println("Inside C's constructor.");
    }
}

class CallingCons {
    public static void main(String args[]) {
        C c = new C();
    }
}

这个程序的输出是:

Inside A's constructor
Inside B's constructor
Inside C's constructor

你问为什么,以及其他答案,imo,并没有真正说出为什么可以调用你的超级构造函数,但只有它是第一行。 原因是你没有真的调用构造函数。 在C ++中,等效的语法是

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

当你看到像这样的初始化子句时,在开放大括号之前,你知道它是特殊的。 它在构造函数的其余部分运行之前运行,实际上在任何成员变量初始化之前运行。 对于Java来说并没有那么不同。 在子类的任何成员被初始化之前,有一种方法可以在构造函数真正启动之前运行一些代码(其他构造函数)。 那样就是把“呼叫”(例如super )放在第一线。 (从某种意义上说, super或者在第一个开放大括号之前是这样的,尽管你之后输入了它,因为它会在你完全构建完成之前执行)。开放大括号之后的任何其他代码(比如int c = a + b; )让编译器说:“哦,好吧,没有其他构造函数,我们可以初始化所有东西。” 所以它会跑掉并初始化你的超类和你的成员,然后在开启大括号后开始执行代码。

如果几行后,它会遇到一些代码,说:“当你构建这个对象时,是的,这里是我希望你传递给基类的构造函数的参数”,它已经太晚了,也没有有道理。 所以你得到一个编译器错误。


只是因为这是继承的哲学。 根据Java语言规范,这是如何定义构造函数的主体:

ConstructorBody:{ExplicitConstructorInvocation opt BlockStatements opt }

构造函数体的第一个陈述可能是:
- 显式调用同一类的另一个构造函数(使用关键字“this”)
- 直接超类(使用关键字“super”)

如果构造函数主体不是以明确的构造函数调用开始的,并且声明的构造函数不是原始类Object的一部分,则构造函数体隐式地以超类构造函数调用“super();”开始,调用构造函数它的直接超类没有任何参数。 等等......将会有一整个构造函数链被调用回到Object的构造函数中; “Java平台中的所有类都是对象的后代”。 这个东西被称为“ 构造器链接 ”。

那为什么呢?
Java以这种方式定义ConstructorBody的原因是它们需要维护对象的层次结构 。 记住继承的定义; 它扩展了一个类。 说到这里,你不能扩展一些不存在的东西。 基础(超类)需要首先被创建,然后你可以派生它(子类)。 这就是为什么他们称他们为父母和孩子班; 没有父母的孩子不能生育。

在技​​术层面上,子类从其父项继承所有成员(字段,方法,嵌套类)。 由于构造函数不是成员(它们不属于对象,它们负责创建对象),因此它们不被子类继承,但可以调用它们。 而且由于在创建对象时只有一个构造函数被执行 。 那么当你创建子类对象时,我们如何保证创建超类? 因此“构造链”的概念; 所以我们有能力从当前构造函数中调用其他构造函数(即超级)。 Java要求这个调用是子类构造函数中的第一行,以维护层次并保证它。 他们假设,如果你没有明确地创建父对象FIRST(就像你忘了它),他们会为你隐式地做。

该检查在编译期间完成。 但是我不确定在运行时会发生什么,我们会得到什么样的运行时错误,当我们明确地尝试从其子类的构造函数中执行一个基构造函数时,Java不会抛出编译错误身体,而不是从第一线...


在构建子对象之前,必须创建父对象。 正如你知道你写这样的课时:

public MyClass {
        public MyClass(String someArg) {
                System.out.println(someArg);
        }
}

它转向下一个(扩展和超级只是隐藏):

public MyClass extends Object{
        public MyClass(String someArg) {
                super();
                System.out.println(someArg);
        }
}

首先我们创建一个Object ,然后将这个对象扩展到MyClass 。 我们无法在Object之前创建MyClass 。 简单的规则是必须在子构造函数之前调用父项的构造函数。 但我们知道类可以有更多的构造函数。 Java允许我们选择一个将被调用的构造函数(它将是super()super(yourArgs...) )。 所以,当你写super(yourArgs...)你重新定义了将被调用来创建父对象的构造函数。 你不能在super()之前执行其他方法,因为对象还不存在(但是在super()一个对象将被创建,你将能够做任何你想做的事)。

那么为什么我们不能在任何方法之后执行this() ? 如你所知, this()是当前类的构造函数。 我们也可以在我们的类中使用不同数量的构造函数,并将它们称为this()this(yourArgs...) 。 正如我所说的每个构造函数都有隐藏方法super() 。 当我们编写我们的自定义super(yourArgs...)我们使用super(yourArgs...)移除super() super(yourArgs...) 。 当我们定义this()this(yourArgs...)我们也在当前构造函数中移除我们的super() ,因为如果super() this()在同一个方法中使用this() ,它会创建多个父对象。 这就是为什么对this()方法施加相同的规则。 它只是将父对象创建重新传递给另一个子构造函数,并且该构造函数为父创建调用super()构造函数。 所以,代码将会是这样的:

public MyClass extends Object{
        public MyClass(int a) {
                super();
                System.out.println(a);
        }
        public MyClass(int a, int b) {
                this(a);
                System.out.println(b);
        }
}

正如其他人所说,你可以像这样执行代码:

this(a+b);

你也可以像这样执行代码:

public MyClass(int a, SomeObject someObject) {
    this(someObject.add(a+5));
}

但是你不能像这样执行代码,因为你的方法还不存在:

public MyClass extends Object{
    public MyClass(int a) {

    }
    public MyClass(int a, int b) {
        this(add(a, b));
    }
    public int add(int a, int b){
        return a+b;
    }
}

Also you are obliged to have super() constructor in your chain of this() methods. You can't have an object creation like this:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}

在调用它的构造函数之前,可以使用匿名初始化块来初始化子项中的字段。 这个例子将演示:

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

class Parent {
    public Parent() {
        System.out.println("In parent");
    }
}

class Child extends Parent {

    {
        System.out.println("In initializer");
    }

    public Child() {
        super();
        System.out.println("In child");
    }
}

这将输出:

在父母身上
在初始化器中
在孩子


我完全同意,限制太强。 使用静态辅助方法(如Tom Hawtin - tackline建议)或将所有“pre-super()计算”推送到参数中的单个表达式中并非总是可行,例如:

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

正如Carson Myers建议的那样,使用“对象未构造”异常会有所帮助,但在每个对象构造过程中检查这个异常会减慢执行速度。 我希望Java编译器能够做出更好的区分(而不是通过禁止if语句,但允许参数中的?-operator),即使这会使语言规范复杂化。


我的猜测是,他们这样做是为了让编写处理Java代码的工具的人们更轻松,并且在一定程度上也是阅读Java代码的人。

如果允许super()this()调用四处移动,则需要检查更多变体。 例如,如果将super()this()调用移动到条件if()它可能必须足够聪明以将隐式super()插入到else 。 如果你调用super()两次,或者同时使用super()this() ,它可能需要知道如何报告错误。 它可能需要禁止接收方的方法调用,直到调用super()this() ,并确定何时变得复杂。

让每个人都做这些额外的工作可能看起来成本高于收益。


我知道我晚了一点,但我已经使用了这个技巧几次(我知道这有点不寻常):

我用一种方法创建一个通用接口InfoRunnable<T>

public T run(Object... args);

如果在将它传递给构造函数之前我需要做一些事情,我只需要这样做:

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));

我通过链接构造函数和静态方法找到了解决这个问题的方法。 我想做的事情看起来像这样:

public class Foo extends Baz {
  private final Bar myBar;

  public Foo(String arg1, String arg2) {
    // ...
    // ... Some other stuff needed to construct a 'Bar'...
    // ...
    final Bar b = new Bar(arg1, arg2);
    super(b.baz()):
    myBar = b;
  }
}

所以基本上是基于构造函数参数构造一个对象,将该对象存储在成员中,并将该对象的方法结果传递给超类的构造函数。 让成员最终决定也是非常重要的,因为这个类的性质是不可变的。 请注意,实际上,构建Bar实际上需要一些中间对象,所以在我的实际用例中它不能简化为一行。

我最终使其工作如下所示:

public class Foo extends Baz {
  private final Bar myBar;

  private static Bar makeBar(String arg1,  String arg2) {
    // My more complicated setup routine to actually make 'Bar' goes here...
    return new Bar(arg1, arg2);
  }

  public Foo(String arg1, String arg2) {
    this(makeBar(arg1, arg2));
  }

  private Foo(Bar bar) {
    super(bar.baz());
    myBar = bar;
  }
}

法定代码,它在调用超级构造函数之前完成执行多个语句的任务。


实际上,它super()是构造函数的第一个语句,因为要确保它的超类在构造子类之前完全形成。即使你没有super()第一条语句,编译器也会为你添加它!


class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

See the example if we are calling the constructor C(int x) then value of z is depend on y if we do not call C() in the first line then it will be the problem for z. z would not be able to get correct value.





constructor