【问题标题】:Is it possible to do computation before super() in the constructor?是否可以在构造函数中的 super() 之前进行计算?
【发布时间】:2010-02-20 20:28:59
【问题描述】:

鉴于我有一个 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 之前完成计算,但在构造函数中?

谢谢。

【问题讨论】:

  • 这被标记为 gwt 的可能原因是什么?因为你是在 gwt 中尝试的??
  • TextBox 是一个 GWT 类,但不,我想它不相关。

标签: java


【解决方案1】:

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

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

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

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

【讨论】:

  • 这似乎可以很好地回答有关如何使用对在构造之后但在 super 之前创建的对象的引用的问题。
  • 这是一个非常方便的模式,并且几乎可以让您完全规避super() 不会出现在构造函数中的其他任何内容之后的要求。在极端情况下,您甚至可以将多个私有构造函数链接在一起,尽管我想不出我需要这样做的任何情况,如果我这样做了,我可能会开始想知道是否有更好的方法来完成我的任务正在做。
  • 是的。其他人已经回答了为什么它在 Java 中是这样工作的,我只是想指出一个特定的解决方法。我认为对于您可以提出的大多数其他 有效 案例,也可以提出可接受的解决方法。现在,与我自己相矛盾的是,有一种情况让我很反感:我正在扩展一个类,它的构造函数有一个 Class 参数。我想打电话给super(this.getClass()),但是由于不允许您引用this,因此该代码是非法的,尽管此时我应该能够知道实际的类似乎完全合理。
  • @waxwing - 它可能看起来“非常合理”,但它需要将 getClass() 方法视为 JLS 中的一个特例。请改用<classname>.class
  • 在这种情况下使用<classname>.class 不是一个选项,因为我正在编写一个人们可以扩展的抽象类,我需要实例的实际类。
【解决方案2】:

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

在您的类中使用私有静态方法,该方法返回超级构造函数的参数并在内部进行检查:

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

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

【讨论】:

    【解决方案3】:

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

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

    herehere 进行了广泛的讨论。

    【讨论】:

    • 有趣的是,听起来 Java 不遗余力地阻止了将代码包装在超类构造函数调用周围的最重要情况之一:确保如果构造函数抛出异常,事情可以清理干净。例如,如果一个对象的构造函数重载从传入的文件中读取数据,派生类构造函数可能需要一个接受文件名的重载,将新打开的文件传递给超类构造函数,然后关闭文件之后。如果超类构造函数抛出,文件如何关闭?
    • @supercat:您必须设计继承或排除继承 [Bloch,EffectiveJava,§17];另见Q&A
    • 如果子类对象的构造需要“获取资源;构造超类对象;释放资源”的模式,应该如何实现?向内部构造函数传递一个实现单接口方法的对象,以便在失败时使用?模糊的可行,也许,以链接一些额外的构造函数调用为代价。
    • 我有幸修复超类或使用组合;如果您提出这个问题,请联系我。
    【解决方案4】:

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

    public class MyClass {
    
        private static Supplier<MyType> myTypeSupplier = () -> {
            return new MyType();
        };
    
        public MyClass() {
            super(clientConfig, myTypeSupplier.get());
        }
    }
    

    【讨论】:

      【解决方案5】:

      之所以允许第二个例子而不是第一个例子,最有可能是为了保持语言的整洁,而不是引入奇怪的规则。

      在调用 super 之前允许任何代码运行将是危险的,因为您可能会弄乱本应初始化但仍未初始化的内容。基本上,我想你可以在调用 super 本身时做很多事情(例如,调用一个静态方法来计算一些需要去构造函数的东西),但是你永远无法使用 not -尚未完全构造的对象,这是一件好事。

      【讨论】:

        【解决方案6】:

        Java 就是这样工作的 :-) 之所以选择这种方式是有技术原因的。在调用 super 之前您不能对局部变量进行计算可能确实很奇怪,但是在 Java 中必须首先分配对象,因此它需要一直到 Object 以便在您意外修改之前正确初始化所有字段他们。

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

        超级(新文本框()); 最终文本框框 = getWidget(); ……做你的事……

        【讨论】:

        • Java 中实际上并不能保证“所有字段都正确初始化”(与 IIUC、C++ 不同),因为超类构造函数可以调用子类覆盖的非final 实例方法。然后,覆盖将看不到子类中的已初始化字段(即使是 final 带有实例初始化程序的字段!)。一些静态分析工具会正确地警告这种情况。
        【解决方案7】:

        这是我的解决方案,它允许创建额外的对象、修改它而不创建额外的类、字段、方法等。

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

        【讨论】:

          猜你喜欢
          • 2012-07-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-30
          • 2013-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多