29

如果我有一堂课:

class A {
   public A() { }
}

和另一个

class B extends A {
   public B() { }
}

有什么办法可以B.B() 打电话A.A()吗?

4

13 回答 13

33

在 Java 中绝对没有办法做到这一点。它会破坏语言规范。

JLS 12 执行 / 12.5 新类实例的创建

就在对新创建对象的引用作为结果返回之前,使用以下过程处理指示的构造函数以初始化新对象:

  1. 为构造函数分配参数 [...]
  2. 如果此构造函数以对同一类中的另一个构造函数的显式构造函数调用开始(使用this),则 [...]
  3. 此构造函数不是以显式构造函数调用同一类中的另一个构造函数开始的(使用this)。如果此构造函数用于除 之外的类 Object则此构造函数将以显式或隐式调用超类构造函数开始(使用super)。
  4. 为此类执行实例初始化程序和实例变量初始化程序 [...]
  5. 执行此构造函数的其余部分 [...]
于 2010-06-03T16:06:47.143 回答
23

您可以实现的最接近所需行为的方法是将通常在构造函数中执行的初始化委托给模板方法,然后您在子类实现中覆盖该方法。例如:

public class A {
  protected Writer writer;

  public A() {
    init();
  }

  protected void init() {
    writer = new FileWriter(new File("foo.txt"));
  }
}

public class B extends A {
  protected void init() {
    writer = new PaperbackWriter();
  }
}

但是,正如其他人所指出的,这通常表明您的设计存在问题,我通常更喜欢这种情况下的组合方法;例如,在上面的代码中,您可以定义构造函数以接受Writer实现作为参数。

于 2010-06-03T16:12:56.140 回答
6

如果您不想调用超类构造函数,那么您的对象模型还有其他问题。

于 2010-06-03T16:08:09.427 回答
1

可能性是您可以调用您选择的超类构造函数。这可以通过显式调用超类构造函数来实现:

super(para_1, para_2,........);

class BaseA {
    BaseA(){
        System.out.println("This is BaseA");
    }
    BaseA(int a){
        System.out.println("This is BaseA a");
    }


}

class A extends BaseA {

    A(){
        super(5);
        System.out.println("This is A");
    }

    public static void main(String[] args) {
        A obj=new A();

    }
}


Output will be:
This is BaseA a
This is A
于 2019-01-09T05:43:50.413 回答
1

Java 反序列化不调用构造函数,但它似乎是基于一些内部 JVM 技巧。

但是,有一个框架可以让您以可移植的方式做到这一点:Objenesis ( http://www.theserverside.com/discussions/thread/44297.html )

我最近在 Spring 中看到了这一点,当使用 CGLIB 代理时,Spring 创建了两个类实例,但构造函数只被调用一次:https ://stackoverflow.com/a/11583641/2557118

此行为是在 Spring 4 中添加的:

基于 CGLIB 的代理类不再需要默认构造函数。通过 objenesis 库提供支持,该库被重新打包并作为 Spring 框架的一部分分发。使用此策略,不再为代理实例调用任何构造函数。

于 2017-08-30T09:48:52.100 回答
0

不,如果可以的话,您的派生对象不会真的是它现在派生的对象吗?is-a 原则将被违反。因此,如果您真的需要它,那么多态性就不是您所追求的。

于 2010-06-03T16:01:21.683 回答
0
  1. 正如另一张海报所指出的,B 没有扩展 A,因此它无论如何都不会调用 A 的构造函数。

  2. 在 Java 中没有办法做到这一点。

  3. 您可能可以等效地完成您想做的事情,如下所示:

a) 在您的层次结构的每个类中,包含一个具有唯一签名的构造函数,该构造函数使用其参数调用超类的构造函数。例如,声明一个类“Noop”和一个将其作为参数的构造函数:

public class NoOp {
}

public class class1 {
    class1() {
        System.out.println("class1() called");
    }
    class1(String x, String y) {
        System.out.println("class1(String, String) called");
}
    class1(NoOp x) {
        System.out.println("class1(NoOp) called");
    }
}

public class class2 extends class1 {
    class2() {
        System.out.println("class2() called");
    }
    class2(String x, String y) {
        System.out.println("class2(String, String) called");
}
    class2(NoOp x) {
        super(x);
        System.out.println("class2(NoOp) called");
    }
}

public class class3 extends class2 {
    class3() {
        System.out.println("class3() called");
    }
    class3(String x, String y) {
        super(new NoOp());
        System.out.println("class3(String, String) called");
    }
    class3(NoOp x) {
        super(x);
        System.out.println("class3(NoOp) called");
    }
    public static void main(String args[]) {
        class3 x = new class3("hello", "world");
    }
}

如果你运行它,你会得到输出

class1(NoOp) called
class2(NoOp) called
class3(String, String) called

因此,您实际上创建了一个 class3 构造函数,它只调用不做任何事情的构造函数。

于 2010-06-03T16:39:16.637 回答
0

不——你不能这样做,你为什么还要这样做?那会弄乱你的对象模型。

无论如何 - 我相信如果您仍然想这样做,那么您将不得不操作生成的字节码......有几个可用的库可以轻松检测字节码。

强烈建议不要这样做...

于 2010-06-03T16:26:02.770 回答
0

Java 中的每个对象都是 Object 的子类(带有大写“O”的对象)。当您创建子类的对象时,将调用超类构造函数。即使您的类没有继承任何其他类,它也隐含地继承了 Object,因此必须调用 Object 构造函数。因此为此目的调用了 super()。

于 2010-06-03T16:32:43.867 回答
0

假设你的意思

class B extends A {
     public B() { }
}

那么确定你可以

class B extends A {
     public B() {
         this(abort());
     }
     private B(Void dummy) {
         /* super(); */
     }
     private static Void abort() {
         throw null;
     }
}

不是很有用。类的接口 [不是 Java 关键字]A说你需要运行它的构造函数来构造它,而不是不合理的。例外是可序列化类的构造没有调用可序列化类的构造函数。

于 2010-06-03T16:34:35.353 回答
0

每个超类都需要构造,除了调用构造函数之外别无他法。

于 2010-06-03T16:02:05.557 回答
0

我认为唯一的方法就是弄乱字节码。
我不确定 Classloader 或 JVM 是否检查是否super()被调用,但是,正如 Bozho 所写,这样做时您可能会以不一致的对象结束。

于 2010-06-03T16:05:40.063 回答
0

我有一个类似的要求,我需要我的子类不要通过超类的构造函数,并且我想要超类的其余好处。由于超级课也是我的,这就是我所做的。

class SuperClass {
    protected SuperClass() {
        init();
    }

    // Added for classes (like ChildClassNew) who do not want the init to be invoked.
    protected SuperClass(boolean doInit) {
        if (doInit)
            init();
    }

    //
}

class ChildClass1 extends SuperClass {
    ChildClass1() {
        // This calls default constructor of super class even without calling super() explicitly.
        // ...
    }
    // ....
}

class ChildClass2 extends SuperClass {
    ChildClass2() {
        // This calls default constructor of super class even without calling super() explicitly.
        // ...
    }
    // ....
}

class ChildClassNew extends SuperClass {
    ChildClassNew() {
        /*
         * This is where I didn't want the super class' constructor to 
         * be invoked, because I didn't want the SuperClass' init() to be invoked.
         * So I added overloaded the SuperClass' constructor where it diesn;t call init().
         * And call the overloaded SuperClass' constructor from this new ChildClassNew.
         */
        super(false);
        // 
        // ...
    }
    // ....
}
于 2014-01-09T17:35:41.023 回答