因此,在Java中,构造函数的第一行HAS是对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不希望让我在一个可能不一致的状态下拥有一个构造对象......但是,在做一个模拟时,我并不关心它。 看来我应该能够做到这一点......或者至少我知道上面的情况对我来说是安全的......或者好像它应该是反正的。

我正在覆盖我在测试类中使用的任何方法,因此我没有使用未初始化变量的风险。

===============>>#1 票数:14 已采纳

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

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

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

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

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

===============>>#2 票数:7

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

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

===============>>#3 票数:6

我知道这是一个老问题,但我喜欢它,因此,我决定给它一个自己的答案。 也许我对为什么不能这样做的理解将有助于讨论和未来读者的有趣问题。

让我从一个失败的对象构造的例子开始。

让我们定义一个A类,这样:

class A {
   private String a = "A";

   public A() throws Exception {
        throw new Exception();
   }
}

现在,让我们假设我们想在try...catch块中创建一个A类型的对象。

A a = null;
try{
  a = new A();
}catch(Exception e) {
  //...
}
System.out.println(a);

显然,此代码的输出将为: null

为什么Java不返回部分构造的A版本? 毕竟,构造函数失败时,对象的name字段已经初始化了,对吗?

好吧,Java无法返回部分构造的A版本,因为该对象未成功构建。 该对象处于不一致状态,因此被Java丢弃。 您的变量A甚至没有初始化,它保持为null。

现在,如您所知,要完全构建新对象,必须首先初始化其所有超类。 如果其中一个超类未能执行,那么对象的最终状态是什么? 无法确定这一点。

看看这个更精细的例子

class A {
   private final int a;
   public A() throws Exception { 
      a = 10;
   }
}

class B extends A {
   private final int b;
   public B() throws Exception {
       methodThatThrowsException(); 
       b = 20;
   }
}

class C extends B {
   public C() throws Exception { super(); }
}

当调用C的构造函数时,如果在初始化B时发生异常,那么最终的int变量b的值是多少?

因此,对象C无法创建,它是虚假的,它是垃圾,它没有完全初始化。

对我来说,这解释了为什么你的代码是非法的。

===============>>#4 票数:1

我不知道Java是如何在内部实现的,但是如果超类的构造函数抛出异常,那么就没有扩展的类的实例。 例如,调用toString()equals()方法是不可能的,因为它们在大多数情况下都是继承的。

Java可能允许在构造函数中使用try / catch来调用super()调用,如果1.覆盖超类中的ALL方法,并且2.不使用super.XXX()子句,但所有声音都太复杂了我。

===============>>#5 票数:1

我不能假设对Java内部有深入的了解,但我的理解是,当编译器需要实例化派生类时,它必须首先创建基础(及其之前的基础(...))然后拍打子类中的扩展。

因此,它甚至不是非连续变量或任何类似的变量的危险。 当您尝试在基类' 构造函数之前在子类'构造函数中执行某些操作时,您基本上要求编译器扩展尚不存在的基础对象实例。

编辑:在您的情况下, MyClass成为基础对象, MyClassMock是一个子类。

===============>>#6 票数:0

我知道这个问题有很多答案,但是我想给出一些小问题,说明为什么不允许这样做,特别是回答为什么Java不允许你这样做。 所以你去......

现在,请记住,必须在子类的构造函数中的其他任何东西之前调用super() ,因此,如果您在super()调用周围使用了trycatch块,则块必须如下所示:

try {
   super();
   ...
} catch (Exception e) {
   super(); //This line will throw the same error...
   ...
}

如果super()try块中失败,那么它必须首先在catch块中执行,以便super在子类的构造函数中的任何内容之前运行。 这会让您遇到与开头时相同的问题:如果抛出异常,则不会捕获它。 (在这种情况下,它只会在catch块中再次抛出。)

现在,Java也不允许使用上述代码。 此代码可以执行第一个超级调用的一半,然后再次调用它,这可能会导致某些超类的一些问题。

现在,Java不允许你抛出异常而不是调用super()的原因是因为异常可以在其他地方被捕获,并且程序将继续而不在你的子类对象上调用super()并且可能因为异常可以将您的对象作为参数并尝试更改尚未初始化的继承实例变量的值。

===============>>#7 票数:-1

解决它的一种方法是调用私有静态函数。 然后可以将try-catch放置在函数体中。

public class Test  {
  public Test()  {
     this(Test.getObjectThatMightThrowException());
  }
  public Test(Object o)  {
     //...
  }
  private static final Object getObjectThatMightThrowException()  {
     try  {
        return  new ObjectThatMightThrowAnException();
     }  catch(RuntimeException rtx)  {
        throw  new RuntimeException("It threw an exception!!!", rtx);
     }
  }
}

  ask by Mike Stone translate from so

未解决问题?本站智能推荐: