2008-08-07 11 views
33

그래서 Java에서는 생성자의 첫 번째 행이 super에 대한 호출이되어야합니다. 암시 적으로 super()를 호출하거나 명시 적으로 다른 생성자를 호출해야합니다. 내가 알고 싶은 것은, 왜 그 주위에 블록을 놓을 수 없습니까?내 super() 호출에서 try 블록을 사용할 수없는 이유는 무엇입니까?

필자가 테스트 한 모의 클래스가있는 경우를 예로 들겠습니다. 기본 생성자는 없지만 테스트를 더 간단하게 만들어야합니다. 또한 생성자에서 Throw 된 예외를 RuntimeException으로 래핑하려고합니다.

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

    // Mocked methods 
} 

그러나 자바는 매우 첫 번째 문이 아닌 불평 :

그럼, 내가하고 싶은 것은 효율적이있다.

내 해결 방법 :

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 
} 

이 가장 좋은 해결 방법인가? 왜 자바는 내가 할 수 없습니까?


자바 모의를하고 그러나 ... 나에게 잠재적으로 일치하지 않는 상태에서 구성 객체를 보자 싶어하지 않는다는 것입니다 "왜"를, 난 상관 없어에로 내 추측 그것에 대해. 내가 위를 할 수 있어야한다고 생각합니다 ... 아니면 최소한 나는 위의 것이 나의 경우에 안전하다는 것을 안다. 또는 어쨌든 그래야만하는 것처럼 보인다.

테스트 된 클래스에서 사용하는 메서드를 재정의하므로 초기화되지 않은 변수를 사용할 위험이 없습니다.

+1

흥미로운 사실은 이것이 순수한 Java 언어 제한이라는 것입니다. 동등한 바이트 코드는 완벽하게 유효합니다. – Antimony

+0

바이트 코드가 여전히 유효합니까? 나는 아래에 설명 된 결과 보안 구멍을 누군가가 착취 한 후 무효화되는 것을 상기합니다. – Joshua

+0

규칙에서 허용하지 않기 때문입니다. [JDK 스펙] (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.10)을 읽으십시오. 컴파일러를 지나치더라도 검증자가이를 거부합니다. –

답변

14

불행히도 컴파일러는 이론적 원리로 작업 할 수 없으며, 귀하의 경우 안전하다는 것을 알고 있더라도 허용한다면 모든 경우에있어 안전해야합니다.

다른 말로하면, 컴파일러는 단지 여러분을 멈추게하지 않고, 안전하지 않고 특별한 처리가 필요하다는 것을 모르는 사람들을 포함하여 모든 사람을 중지시키는 것입니다. 모든 언어에는 일반적으로 할 수있는 방법이 있으므로 안전하지 않은 가지를 처리하는 방법을 알고있는 경우에는 다른 이유가 있습니다. C# .NET에서

이 유사한 조항이 있고, 기본 생성자를 호출하는 생성자를 선언 할 수있는 유일한 방법은 이것이다 :

그렇게에서
public ClassName(...) : base(...) 

, 기본 생성자의 몸체 전에 호출됩니다 생성자이며이 순서를 변경할 수 없습니다.

+1

그냥 catch 블록에서 사용하지 못하게하는 것이 어떨까요? 이는 예외를 래핑하는 일반적인 경우를 다룹니다. – Antimony

2

Java가 내부적으로 어떻게 구현되는지는 잘 모르지만 수퍼 클래스의 생성자가 예외를 throw하면 확장하는 클래스의 인스턴스가 없습니다. 예를 들어 대부분의 경우 상속되므로 toString() 또는 equals() 메서드를 호출하는 것은 불가능합니다.

Java는 생성자에서 super() 호출을 try/catch 할 수 있습니다. 1. 수퍼 클래스에서 모든 메소드를 오버라이드하고 2. super.XXX() 절을 사용하지 않지만 모든 나에게 너무 복잡하게 들린다.

2

Java 내부를 깊이 이해할 수는 없지만 컴파일러에서 파생 클래스를 인스턴스화해야 할 때 처음에는 기본 클래스 (및 그 전에 기본 클래스 (. ..)) 그리고 나서 서브 클래스에서 만들어진 확장을 치십시오.

그래서 변수가없는 변수 나 그와 같은 것이 전혀 위험하지 않습니다. 하위 클래스의 생성자 에서 무언가를 수행하려고 시도 할 때 기본 클래스 '생성자 전에 작성하면 기본적으로 컴파일러에게 아직 존재하지 않는 기본 객체 인스턴스를 확장하도록 요청합니다.

편집 : 귀하의 경우에는, MyClass에는 기본 오브젝트되고, MyClassMock는 서브 클래스입니다.

5

누군가가 신뢰할 수없는 코드에서 새로운 SecurityManager 개체를 만들지 못하게했습니다.

public class Evil : SecurityManager { 
    Evil() 
    { 
     try { 
     super(); 
     } catch { Throwable t } 
     { 
     } 
    } 
} 
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로 유지됩니다.

아시다시피 새로운 객체를 완전히 빌드하려면 모든 수퍼 클래스를 먼저 초기화해야합니다. 수퍼 클래스 중 하나가 실행에 실패하면 객체의 최종 상태는 어떻습니까? 그것을 결정하는 것은 불가능합니다.

B를 초기화하는 동안 예외가 발생했을 경우 C의 생성자가 호출이 더 정교한 예를

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

에서 봐, 무슨 일 최종 int 변수 b의 값이 될 것인가?

이와 같이 개체 C는 만들 수 없으며 위조되어 휴지통이며 완전히 초기화되지 않았습니다.

나를 위해, 이것은 귀하의 코드가 불법 인 이유를 설명합니다.

-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); 
    } 
    } 
} 
+0

이유를 정성스럽게 관리 하시겠습니까? – Unheilig

+0

나는 조금 정교하게 만들었다. 충분히? – aliteralmind

+0

여기서 개인용 정적 함수를 호출하는 것이 왜 효과가 있습니까? 그리고 OP가 작동하지 않는다고 생각하는 코드의 문제점은 무엇입니까? – Unheilig

0

나는이 질문은 많은 답을 가지고 알아,하지만 난이 허용되지 왜 내 작은 재미있는 이야기를주고 싶습니다. 당신이 당신의 super() 호출 주위에 trycatch 블록을 사용 않은 경우 그래서 여기 당신이 지금 ...

super()이 서브 클래스의 생성자에서 다른 어떤 전에 호출되어야 함을 명심 때문에, 블록은해야 할 것 다음과 같이 :

슈퍼() fails in the 시도
try { 
    super(); 
    ... 
} catch (Exception e) { 
    super(); //This line will throw the same error... 
    ... 
} 

경우 block, it HAS to be executed first in the 캐치 block, so that 슈퍼 runs before anything in your subclass의 생성자입니다. 이로 인해 처음부터 같은 문제가 발생합니다. 예외가 발생하면 잡히지 않습니다. (이 경우 catch 블록에서 다시 throw됩니다.)

이제 위 코드는 Java에서 허용되지 않습니다. 이 코드는 첫 번째 수퍼 호출의 절반을 실행 한 다음 다시 호출하여 수퍼 클래스에 문제가 발생할 수 있습니다.

이제, 자바는 예외 대신을 던져 이유를하지 않는 점을 제외하고는 다른 곳에서 잡힐 수 있고, 프로그램, 서브 클래스 객체에 super()를 호출하지 않고을 을 계속하고 있기 때문에 super()를 호출하는 것은 이는 예외가 객체를 매개 변수로 사용하고 아직 초기화되지 않은 상속 된 인스턴스 변수의 값을 변경하려고하기 때문일 수 있습니다.