是否可以在构造函数中的super()之前进行计算?

时间:2010-02-20 20:29:00

标签: java

鉴于我有一个类Base,它有一个带有TextBox对象的参数构造函数作为它的参数。如果我有一个简单的以下形式的课程:

public class Simple extends Base {
  public Simple(){
    TextBox t = new TextBox();
    super(t);
    //wouldn't it be nice if I could do things with t down here?
  }
}

我会收到一个错误,告诉我对super的调用必须是构造函数中的第一个调用。然而,奇怪的是,我可以做到这一点。

public class Simple extends Base {
  public Simple(){
    super(new TextBox());
  }
}

为什么这是被允许的,但第一个例子不是?我可以理解需要首先设置子类,也许不允许在调用超级构造函数之前实例化对象变量。但是t显然是一个方法(局部)变量,为什么不允许呢?

有没有办法解决这个限制?有没有一种安全的方法可以将变量保存到您可能构造的内容之前调用super但是在您输入构造函数之后?或者,更一般地说,允许在实际调用super之前进行计算,但是在构造函数中?

谢谢。

7 个答案:

答案 0 :(得分:69)

是的,您的简单案例有一个解决方法。您可以创建一个私有构造函数,将TextBox作为参数并从公共构造函数中调用它。

public class Simple extends Base {
    private Simple(TextBox t) {
        super(t);
        // continue doing stuff with t here
    }

    public Simple() {
        this(new TextBox());
    }
}

对于更复杂的东西,您需要使用工厂或静态工厂方法。

答案 1 :(得分:24)

在超级调用之前,我遇到了相同的计算问题。有时您想在调用super()之前检查一些条件。例如,您有一个在创建时使用大量资源的类。子类需要一些额外的数据,并且可能希望在调用超级构造函数之前先检查它们。有一个简单的方法来解决这个问题。可能看起来有点奇怪,但效果很好:

在类中使用一个私有静态方法,它返回超级构造函数的参数并在里面进行检查:

public class Simple extends Base {
  public Simple(){
    super(createTextBox());
  }

  private static TextBox createTextBox() {
    TextBox t = new TextBox();
    t.doSomething();
    // ... or more
    return t;
  }
}

答案 2 :(得分:8)

语言为required,以确保超级类可靠地构建 first 。特别是,“如果构造函数没有显式调用超类构造函数,Java编译器会自动插入对超类的无参数构造函数的调用。”

在您的示例中,超类可能在构造时依赖t的状态。您可以随时索取副本。

广泛讨论herehere

答案 3 :(得分:0)

第二个例子被允许但不是第一个例子的原因最有可能是保持语言整洁而不引入奇怪的规则。

在调用super之前允许任何代码运行会很危险,因为你可能会搞乱应该初始化但仍然没有的东西。基本上,我猜你可以在超级本身的调用中做很多事情(比如调用一个静态方法来计算一些需要去构造函数的东西),但是你永远不能使用任何东西。 - 完全构造的物体,这是一件好事。

答案 4 :(得分:0)

这就是Java的工作方式:-)技术上有理由说明为什么选择这种方式。在调用super之前你不能对locals进行计算可能确实很奇怪,但是在Java中必须首先分配对象,因此它需要一直到Object,以便在你意外地修改之前正确初始化所有字段。它们。

在你的情况下,大多数情况下,getter允许你访问你给super()的参数。所以你会用这个:

super( new TextBox() );
final TextBox box = getWidget();
... do your thing...

答案 5 :(得分:0)

您可以定义静态供应商lambda,其中可以包含更复杂的逻辑。

public class MyClass {

    private static Supplier<MyType> myTypeSupplier = () -> {
        return new MyType();
    };

    public MyClass() {
        super(clientConfig, myTypeSupplier.get());
    }
}

答案 6 :(得分:0)

这是我的解决方案,它允许创建其他对象,对其进行修改而无需创建其他类,字段,方法等。

class TextBox {
    
}

class Base {

    public Base(TextBox textBox) {
        
    }
}

public class Simple extends Base {

    public Simple() {
        super(((Supplier<TextBox>) () -> {
            var textBox = new TextBox();
            //some logic with text box
            return textBox;        
        }).get());
    }
}