생성자에서 super () 전에 계산을 수행 할 수 있습니까?


81

TextBox 개체를 인수로 사용하는 단일 인수 생성자가있는 Base 클래스가 있다고 가정합니다. 다음 형식의 Simple 클래스가있는 경우 :

public class Simple extends Base {
  public Simple(){
    TextBox t = new TextBox();
    super(t);
    //wouldn't it be nice if I could do things with t down here?
  }
}

super에 대한 호출이 생성자의 첫 번째 호출이어야한다는 오류가 표시됩니다. 그러나 이상하게도 나는 이것을 할 수 있습니다.

public class Simple extends Base {
  public Simple(){
    super(new TextBox());
  }
}

이것이 허용되지만 첫 번째 예는 허용되지 않는 이유는 무엇입니까? 먼저 하위 클래스를 설정해야하고 수퍼 생성자가 호출되기 전에 개체 변수가 인스턴스화되지 않도록해야한다는 것을 이해할 수 있습니다. 하지만 t는 분명히 메서드 (로컬) 변수이므로 허용하지 않는 이유는 무엇입니까?

이 제한을 피할 수있는 방법이 있습니까? super를 호출하기 전에 생성 할 수 있지만 생성자를 입력 한 후에 구성 할 수있는 변수를 유지하는 안전하고 좋은 방법이 있습니까? 또는 더 일반적으로 super가 실제로 호출되기 전에 계산을 수행 할 수 있지만 생성자 내에서 수행 할 수 있습니까?

감사합니다.


3
이것이 gwt로 태그 된 가능한 이유는 무엇입니까? gwt에서 시도했기 때문에 ??

2
TextBox는 GWT 클래스 였지만 아니요, 관련성이 없다고 생각합니다.
Stephen Cagle

답변:


89

예, 간단한 사례에 대한 해결 방법이 있습니다. TextBox인수로 사용하는 개인 생성자를 만들고 공용 생성자에서 호출 할 수 있습니다.

public class Simple extends Base {
    private Simple(TextBox t) {
        super(t);
        // continue doing stuff with t here
    }

    public Simple() {
        this(new TextBox());
    }
}

더 복잡한 작업의 경우 팩토리 또는 정적 팩토리 메서드를 사용해야합니다.


1
이것은 생성 후 슈퍼 이전에 생성 된 객체에 대한 참조를 사용하는 방법에 관한 질문에 대한 좋은 대답으로 보입니다.
Stephen Cagle

3
이것은 매우 편리한 패턴이며 super()생성자에서 다른 것 뒤에 나타나지 않는 요구 사항을 완전히 우회 할 수 있습니다 . 극단적 인 경우에는 여러 개인 생성자를 함께 연결할 수도 있습니다.하지만 그렇게해야하는 상황을 생각할 수는 없습니다. 만약 그렇게했다면 내가 무엇을 성취 할 수있는 더 좋은 방법이 있는지 궁금해지기 시작할 것입니다. 하고 있었다.
Laurence Gonsalves

예. 다른 사람들은 Java에서 이와 같이 작동하는 이유에 대해 이미 대답 했으며 특정 해결 방법을 지적하고 싶었습니다. 다른 대부분의 유효한 경우에 대해 생각해 낼 수있는 해결 방법을 제시하는 것도 가능 하다고 생각합니다 . 이제 나 자신과 모순되는 한 가지 경우가 있습니다. 생성자가 Class 매개 변수를 가진 클래스를 확장하고있었습니다. 을 (를) 호출 super(this.getClass())하고 싶었지만 참조 할 수 없기 this때문에이 시점에서 실제 클래스를 알 수 있어야한다는 것이 완벽하게 합리적으로 보이지만 해당 코드는 불법이었습니다.
waxwing

@waxwing- "완벽하게 합리적"인 것처럼 보일 수 있지만 getClass()JLS에서 메서드를 특별한 경우로 취급해야합니다 . <classname>.class대신 사용하십시오 .
Stephen C

<classname>.class사람들이 확장 할 수있는 추상 클래스를 작성하고 있고 인스턴스의 실제 클래스가 필요했기 때문에이 경우 사용 은 옵션이 아닙니다.
waxwing

33

슈퍼 호출 전에 계산에 동일한 문제가있었습니다. 때로는 전화하기 전에 몇 가지 조건을 확인하고 싶을 때super() . 예를 들어 생성시 많은 리소스를 사용하는 클래스가 있습니다. 하위 클래스는 추가 데이터를 원하며 수퍼 생성자를 호출하기 전에 먼저 데이터를 확인하려고 할 수 있습니다. 이 문제를 해결하는 간단한 방법이 있습니다. 약간 이상하게 보일 수 있지만 잘 작동합니다.

슈퍼 생성자의 인수를 반환하고 내부에서 확인하는 클래스 내부의 비공개 정적 메서드를 사용합니다.

public class Simple extends Base {
  public Simple(){
    super(createTextBox());
  }

  private static TextBox createTextBox() {
    TextBox t = new TextBox();
    t.doSomething();
    // ... or more
    return t;
  }
}

8

되어 필요한 슈퍼 클래스가 안정적으로 구축되어 있는지 확인하기 위해 언어에 의해 처음 . 특히 "생성자가 명시 적으로 수퍼 클래스 생성자를 호출하지 않으면 Java 컴파일러는 수퍼 클래스의 인수가없는 생성자에 대한 호출을 자동으로 삽입합니다."

귀하의 예에서 수퍼 클래스는 t생성 시점 의 상태에 의존 할 수 있습니다 . 나중에 언제든지 사본을 요청할 수 있습니다.

여기여기에 광범위한 논의가 있습니다 .


2
흥미롭게도 Java가 슈퍼 클래스 생성자 호출 주위에 코드를 래핑하는 것이 유용한 가장 중요한 경우 중 하나를 방지하기 위해 빠져 나가는 것처럼 들립니다. 생성자가 예외를 throw하면 모든 것이 정리 될 수 있도록 보장합니다. 예를 들어 객체의 생성자 오버로드 중 하나가 전달 된 파일에서 데이터를 읽는 경우 파생 클래스 생성자는 파일 이름을 받아들이고 수퍼 클래스 생성자에 새로 열린 파일을 전달한 다음 파일을 닫는 오버로드를 원할 수 있습니다. 기후. 슈퍼 클래스 생성자가 던지면 어떻게 파일을 닫을 수 있습니까?
supercat

@supercat : 상속을 위해 설계하거나이를 배제해야합니다 [Bloch, EffectiveJava , §17]; 이 Q & A 도 참조하십시오 .
trashgod

서브 클래스 객체의 생성에 "자원 획득, 수퍼 클래스 객체 생성, 자원 해제"패턴이 필요한 경우 어떻게 구현해야합니까? 실패 할 경우 사용할 단일 인터페이스 메서드를 구현하는 개체를 내부 생성자에 전달 하시겠습니까? 추가 수준의 생성자 호출을 연결하는 대신 막연하게 실행 가능합니다.
supercat

나는 슈퍼 클래스를 고치거나 컴포지션을 사용할 수있는 사치가 있었다. 이 질문을 제기하면 나에게 핑.
trashgod

1

더 복잡한 논리를 포함 할 수있는 정적 공급자 람다를 정의 할 수 있습니다.

public class MyClass {

    private static Supplier<MyType> myTypeSupplier = () -> {
        return new MyType();
    };

    public MyClass() {
        super(clientConfig, myTypeSupplier.get());
    }
}

0

두 번째 예제는 허용되지만 첫 번째 예제는 허용되지 않는 이유는 언어를 깔끔하게 유지하고 이상한 규칙을 도입하지 않을 가능성이 높습니다.

super가 호출되기 전에 코드가 실행되도록 허용하는 것은 초기화되어야했지만 아직되지 않은 것들을 망칠 수 있기 때문에 위험 할 것입니다. 기본적으로 super 자체에 대한 호출에서 많은 작업을 수행 할 수 있다고 생각합니다 (예 : 생성자로 이동해야하는 일부 항목을 계산하기위한 정적 메서드 호출). 그러나 그렇지 않은 경우에는 아무것도 사용할 수 없습니다. -아직 완전하게 만들어진 물체는 좋은 것입니다.


0

이것이 자바가 작동하는 방식입니다. :-)이 방식으로 선택된 이유가 있습니다. super를 호출하기 전에 로컬에서 계산을 수행 할 수 없다는 것은 참 이상 할 수 있지만 Java에서는 먼저 객체를 할당해야하므로 실수로 수정하기 전에 모든 필드가 올바르게 초기화되도록 Object까지 계속 이동해야합니다. 그들.

귀하의 경우에는 대부분의 경우 super ()에 부여한 매개 변수에 액세스 할 수있는 게터가 있습니다. 따라서 이것을 사용합니다.

super (new TextBox ());
최종 텍스트 상자 상자 = getWidget ();
... 당신의 일을 ...

2
수퍼 클래스 생성자가 final서브 클래스가 재정의 하는 비 인스턴스 메소드를 호출 할 수 있기 때문에 "모든 필드가 올바르게 초기화되었습니다"라는 사실은 Java (IIUC, C ++와 달리)에서 실제로 보장되지 않습니다 . 그러면 재정의는 하위 클래스에서 초기화 된 필드를 볼 수 없습니다 ( final인스턴스 이니셜 라이저가있는 필드도 !). 일부 정적 분석 도구는이 상황에 대해 올바르게 경고합니다.
Jesse Glick

0

이것은 추가 개체를 만들고 추가 클래스, 필드, 메서드 등을 만들지 않고도 수정할 수있는 내 솔루션입니다.

class TextBox {
    
}

class Base {

    public Base(TextBox textBox) {
        
    }
}

public class Simple extends Base {

    public Simple() {
        super(((Supplier<TextBox>) () -> {
            var textBox = new TextBox();
            //some logic with text box
            return textBox;        
        }).get());
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.