문제

따라서 Java에서 생성자의 첫 번째 줄은 super에 대한 호출이어야 합니다.암시적으로 super()를 호출하거나 명시적으로 다른 생성자를 호출할 수 있습니다.제가 알고 싶은 것은 왜 그 주위에 try 블록을 넣을 수 없는 걸까요?

내 구체적인 사례는 테스트를 위한 모의 클래스가 있다는 것입니다.기본 생성자는 없지만 테스트를 더 쉽게 읽을 수 있도록 만들고 싶습니다.또한 생성자에서 발생한 예외를 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(...)

이렇게 하면 기본 생성자가 생성자 본문보다 먼저 호출되며 이 순서를 변경할 수 없습니다.

다른 팁

누군가가 새로운 것을 생성하는 것을 방지하기 위해 수행되었습니다. SecurityManager 신뢰할 수 없는 코드의 개체입니다.

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

오래된 질문인 건 알지만 마음에 들어서 스스로 답변을 드리기로 했습니다.아마도 이것이 수행될 수 없는 이유에 대한 나의 이해는 토론과 귀하의 흥미로운 질문에 대한 미래의 독자들에게 도움이 될 것입니다.

실패한 객체 생성의 예부터 시작하겠습니다.

다음과 같이 클래스 A를 정의해 보겠습니다.

class A {
   private String a = "A";

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

이제 A 유형의 객체를 생성한다고 가정해 보겠습니다. try...catch 차단하다.

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는 생성될 수 없으며, 가짜이고, 쓰레기이며, 완전히 초기화되지 않았습니다.

나에게는 이것이 귀하의 코드가 불법인 이유를 설명합니다.

Java 내부를 깊이 이해할 수는 없지만 컴파일러가 파생 클래스를 인스턴스화해야 할 때 먼저 기본(그리고 그 이전의 기본(...))을 만들어야 한다는 것이 내 이해입니다. 그런 다음 하위 클래스에서 만든 확장을 적용합니다.

따라서 초기화되지 않은 변수나 그와 유사한 것의 위험조차 없습니다.하위 클래스의 생성자에서 무언가를 하려고 할 때 ~ 전에 기본 클래스' 건설자, 기본적으로 아직 존재하지 않는 기본 개체 인스턴스를 확장하도록 컴파일러에 요청하는 것입니다.

편집:귀하의 경우에는 내 수업 기본 객체가 되고, MyClassMock 하위 클래스입니다.

Java가 내부적으로 어떻게 구현되는지는 모르지만 슈퍼클래스의 생성자가 예외를 발생시키면 확장한 클래스의 인스턴스가 없습니다.전화를 거는 것은 불가능할 것입니다. toString() 또는 equals() 예를 들어 메소드는 대부분의 경우 상속되기 때문입니다.

Java는 1인 경우 생성자에서 super() 호출 주위에 try/catch를 허용할 수 있습니다.2. 슈퍼클래스의 모든 메서드를 재정의합니다.super.XXX() 절을 사용하지 않지만 그 모든 것이 나에게는 너무 복잡하게 들립니다.

이 질문에 수많은 답변이 있다는 것을 알고 있지만 이것이 허용되지 않는 이유, 특히 Java가 이를 허용하지 않는 이유에 대한 간단한 간단한 정보를 제공하고 싶습니다.그럼 여기요...

이제 명심하세요. super() 하위 클래스의 생성자에서 다른 것보다 먼저 호출되어야 하므로, try 그리고 catch 당신 주변의 블록들 super() 호출하면 블록은 다음과 같아야 합니다.

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

만약 슈퍼()fails in the노력하다block, it HAS to be executed first in the잡다block, so that감독자runs before anything in your subclass생성자.이로 인해 처음에 겪었던 것과 동일한 문제가 발생합니다.예외가 발생하면 포착되지 않습니다.(이 경우에는 catch 블록에 다시 던져집니다.)

이제 위 코드는 Java에서도 허용되지 않습니다.이 코드는 첫 번째 슈퍼 호출의 절반을 실행한 다음 다시 호출할 수 있으며, 이로 인해 일부 슈퍼 클래스에 문제가 발생할 수 있습니다.

이제 Java에서 예외 발생을 허용하지 않는 이유 대신에 부름의 super() 예외가 다른 곳에서 포착될 수 있고 프로그램이 계속 진행되기 때문입니다. 전화도 없이 super() 하위 클래스 객체에서 예외가 개체를 매개 변수로 사용하고 아직 초기화되지 않은 상속된 인스턴스 변수의 값을 변경하려고 시도할 수 있기 때문일 수 있습니다.

이 문제를 해결하는 한 가지 방법은 개인 정적 함수를 호출하는 것입니다.그런 다음 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);
     }
  }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top