생성자에서 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.