为什么我不能在我的 super() 调用周围使用 try 块?

2022-08-31 23:43:12

因此,在Java中,构造函数的第一行必须是对超级...无论是隐式调用 super(),还是显式调用另一个构造函数。我想知道的是,为什么我不能围绕这一点设置一个尝试块?

我的具体情况是,我有一个用于测试的模拟类。没有默认构造函数,但我希望有一个使测试更易于阅读。我还想将从构造函数引发的异常包装到运行时异常中。

所以,我想做的是有效地:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

但Java抱怨说,super并不是第一个声明。

我的解决方法:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

这是最好的解决方法吗?为什么Java不允许我做前者?


我对“为什么”的最佳猜测是,Java不想让我有一个可能处于不一致状态的构造对象......但是,在做模拟时,我不在乎这一点。看来我应该能够做到以上...或者至少我知道以上对我的情况是安全的...或者似乎无论如何都应该如此。

我正在重写我从测试类中使用的任何方法,因此不存在使用未初始化变量的风险。


答案 1

不幸的是,编译器不能在理论原则上工作,即使你可能知道它在你的案例中是安全的,如果他们允许它,它必须对所有情况都是安全的。

换句话说,编译器不仅阻止了你,还阻止了所有人,包括所有那些不知道它是不安全的,需要特殊处理的人。这可能还有其他原因,因为所有语言通常都有办法做不安全的事情,如果一个人知道如何处理它们。

在 C# .NET 中也有类似的规定,声明调用基构造函数的构造函数的唯一方法是:

public ClassName(...) : base(...)

这样,基构造函数将在构造函数主体之前调用,并且您无法更改此顺序。


答案 2

这样做是为了防止某人从不受信任的代码创建新对象。SecurityManager

public class Evil : SecurityManager {
  Evil()
  {
      try {
         super();
      } catch { Throwable t }
      {
      }
   }
}