为什么在我的super()调用周围不能使用try块?
因此,在Java中,构造函数的第一行必须是对super的调用...是隐式调用super()还是显式调用另一个构造函数。我想知道的是,为什么我不能对此尝试一下呢?
我的具体情况是我有一个模拟类进行测试。没有默认的构造函数,但是我希望它使测试更易于阅读。我还想将构造函数抛出的异常包装到RuntimeException中。
所以,我想做的实际上是这样的:
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不想让我在潜在的不一致状态下拥有一个已构造的对象……但是,在进行模拟时,我并不关心这一点。看来我应该能够执行以上操作……或者至少我知道以上内容对于我的情况而言是安全的……或者似乎无论如何应该如此。
我将覆盖测试类中使用的所有方法,因此使用未初始化的变量没有任何风险。
解决方案
回答
不幸的是,编译器无法根据理论原理进行工作,即使我们可能知道在情况下它是安全的,但如果他们允许的话,它在所有情况下都必须是安全的。
换句话说,编译器不仅停止了工作,还在停止所有人,包括所有不知道它不安全并且需要特殊处理的人。可能还有其他原因,因为如果知道如何处理所有语言,所有语言通常都有处理不安全行为的方法。
在C.NET中,有类似的规定,而声明调用基本构造函数的构造函数的唯一方法是:
public ClassName(...) : base(...)
这样,基本构造函数将在构造函数的主体之前被调用,并且我们不能更改此顺序。
回答
我不知道Java是如何在内部实现的,但是如果超类的构造函数引发异常,则我们没有扩展该类的实例。例如,不可能调用toString()
或者equals()
方法,因为它们在大多数情况下都是继承的。
如果1.我们覆盖超类的所有方法,并且2.我们不使用super.XXX()子句,则Java可能允许在构造函数中的super()调用周围进行try / catch,但听起来似乎太复杂了,以至于我。
回答
我不能以为对Java内部结构有深入的了解,但是据我了解,当编译器需要实例化派生类时,它必须首先创建基础(以及其基础(在...之前)。然后拍打在子类中进行的扩展。
因此,根本没有未初始化变量或者类似变量的危险。当我们尝试在基类的构造函数之前在子类的构造函数中执行某些操作时,我们基本上是在要求编译器扩展尚不存在的基对象实例。
编辑:在情况下,MyClass成为基础对象,而MyClassMock是子类。
回答
这样做是为了防止某人从不受信任的代码创建新的" SecurityManager"对象。
public class Evil : SecurityManager { Evil() { try { super(); } catch { Throwable t } { } } }