Program Tip

왜 this ()와 super ()가 생성자의 첫 번째 문장이어야합니까?

programtip 2020. 10. 3. 11:34
반응형

왜 this ()와 super ()가 생성자의 첫 번째 문장이어야합니까?


Java는 생성자에서 this () 또는 super ()를 호출하는 경우 첫 번째 명령문이어야합니다. 왜?

예를 들면 :

public class MyClass {
    public MyClass(int x) {}
}

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
    }
}

Sun 컴파일러는 "super에 대한 호출은 생성자의 첫 번째 문이어야합니다"라고 말합니다. Eclipse 컴파일러는 "생성자 호출이 생성자의 첫 번째 명령문이어야합니다"라고 말합니다.

그러나 코드를 약간 다시 정렬하면이 문제를 해결할 수 있습니다.

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        super(a + b);  // OK
    }
}

다음은 또 다른 예입니다.

public class MyClass {
    public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
    public MySubClassA(Object item) {
        // Create a list that contains the item, and pass the list to super
        List list = new ArrayList();
        list.add(item);
        super(list);  // COMPILE ERROR
    }
}

public class MySubClassB extends MyClass {
    public MySubClassB(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(Arrays.asList(new Object[] { item }));  // OK
    }
}

따라서 super를 호출하기 전에 로직을 실행하는 것을 막지않습니다 . 단일 표현식에 맞출 수없는 논리를 실행하는 것을 막을뿐입니다.

을 호출하는 것과 유사한 규칙이 있습니다 this(). 컴파일러는 "이 호출은 생성자의 첫 번째 문이어야합니다"라고 말합니다.

컴파일러에 이러한 제한이있는 이유는 무엇입니까? 컴파일러에 이러한 제한이 없다면 나쁜 일이 발생하는 코드 예제를 제공 할 수 있습니까?


상위 클래스 ' constructor는 하위 클래스보다 먼저 호출되어야합니다 constructor. 이렇게하면 생성자의 부모 클래스에서 메서드를 호출하는 경우 부모 클래스가 이미 올바르게 설정되었는지 확인할 수 있습니다.

당신이하려는 것은 슈퍼 생성자에 args를 전달하는 것은 완벽하게 합법적입니다. 당신이하고있는 것처럼 그 args를 인라인으로 생성하거나 생성자에 전달한 다음 그것들을 전달하면됩니다 super:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

컴파일러가 이것을 강제하지 않았다면 다음과 같이 할 수 있습니다.

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

parent클래스에 기본 생성자가있는 경우 super에 대한 호출이 compiler. Java의 모든 클래스는에서 상속되기 때문에 Object객체 생성자는 어떻게 든 호출되어야하며 먼저 실행되어야합니다. 컴파일러에 의한 super () 자동 삽입이이를 허용합니다. super가 먼저 나타나도록 강제하면 생성자 본문이 다음과 같은 올바른 순서로 실행됩니다. Object-> Parent-> Child-> ChildOfChild-> SoOnSoForth


생성자와 정적 메서드를 연결하여이 문제를 해결하는 방법을 찾았습니다. 내가하고 싶었던 것은 다음과 같았다.

public class Foo extends Baz {
  private final Bar myBar;

  public Foo(String arg1, String arg2) {
    // ...
    // ... Some other stuff needed to construct a 'Bar'...
    // ...
    final Bar b = new Bar(arg1, arg2);
    super(b.baz()):
    myBar = b;
  }
}

따라서 기본적으로 생성자 매개 변수를 기반으로 객체를 생성하고, 객체를 멤버에 저장하고, 해당 객체에 대한 메서드의 결과를 super의 생성자에 전달합니다. 멤버를 최종적으로 만드는 것도 클래스의 특성이 불변이기 때문에 합리적으로 중요했습니다. 실제로 Bar를 구성하려면 실제로 몇 개의 중간 개체가 필요하므로 실제 사용 사례에서는 한 줄로 줄일 수 없습니다.

나는 그것을 다음과 같이 작동하게 만들었다.

public class Foo extends Baz {
  private final Bar myBar;

  private static Bar makeBar(String arg1,  String arg2) {
    // My more complicated setup routine to actually make 'Bar' goes here...
    return new Bar(arg1, arg2);
  }

  public Foo(String arg1, String arg2) {
    this(makeBar(arg1, arg2));
  }

  private Foo(Bar bar) {
    super(bar.baz());
    myBar = bar;
  }
}

합법적 인 코드이며 슈퍼 생성자를 호출하기 전에 여러 문을 실행하는 작업을 수행합니다.


JLS가 그렇게 말하고 있기 때문입니다. JLS를 호환 가능한 방식으로 변경할 수 있습니까? 예.

그러나 이미 충분히 복잡한 언어 사양을 복잡하게 만들 수 있습니다. 그것은 매우 유용한 일이 아니며 주위에 방법이 있습니다 (정적 메서드 또는 람다 식의 결과로 다른 생성자를 호출하십시오 this(fn())-메서드는 다른 생성자보다 먼저 호출되므로 슈퍼 생성자도 호출됩니다). 따라서 변경을 수행하는 힘 대 중량 비율은 바람직하지 않습니다.

이 규칙만으로는 수퍼 클래스가 생성을 완료하기 전에 필드 사용을 방지하지 않습니다.

이러한 불법적 인 예를 고려하십시오.

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

이 예는 합법적이지만 "잘못된"예입니다.

class MyBase {
    MyBase() {
        fn();
    }
    abstract void fn();
}
class MyDerived extends MyBase {
    void fn() {
       // ???
    }
}

위의 예 MyDerived.fn에서 MyDerived생성자 로부터 인수 필요한 경우 ThreadLocal. ; (

덧붙여서, Java 1.4부터는 this내부 클래스 슈퍼 생성자가 호출되기 전에 외부를 포함하는 합성 필드 가 할당됩니다. 이로 인해 NullPointerException이전 버전을 대상으로 컴파일 된 코드에서 특이한 이벤트가 발생했습니다 .

또한 안전하지 않은 출판물이있는 경우 예방 조치를 취하지 않는 한 다른 스레드에서 구성을 다시 정렬 할 수 있습니다.

편집 2018년 3월 : 메시지에서 기록 : 건설 및 검증 오라클은이 제한을 제안한다 제거 할 (그러나 C #을 달리 this할 것이다 확실히 할당되지 않은 생성자 체인 전) (DU).

역사적으로 this () 또는 super ()는 생성자에서 첫 번째 여야합니다. 이 제한은 결코 대중적이지 않았고 자의적인 것으로 인식되었습니다. 이 제한에 기여한 invokespecial의 확인을 포함하여 여러 가지 미묘한 이유가 있습니다. 수년 동안 우리는 레코드뿐만 아니라 모든 생성자에 대해이 제한을 해제하는 것이 실용적이되는 지점까지 VM 수준에서 이러한 문제를 해결했습니다.


나는 (a) 부분적으로 구성된 객체를 사용하도록 허용되고 (b) 부모 클래스의 생성자가 "새로 고침"에서 생성하도록 강제하는 것을 방지하는 것이 (Java 사양 차임에 익숙한 사람) 상당히 확신합니다. "개체.

"나쁜"것의 몇 가지 예는 다음과 같습니다.

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}

이것이 상속 철학이기 때문입니다. 그리고 Java 언어 사양에 따르면 생성자의 본문이 다음과 같이 정의됩니다.

ConstructorBody : {ExplicitConstructorInvocation opt    BlockStatements opt }

생성자 본문의 첫 번째 문은 다음 중 하나 일 수 있습니다.

  • 동일한 클래스의 다른 생성자의 명시 적 호출 ( "this"키워드 사용) 또는
  • 직접 수퍼 클래스의 명시 적 호출 ( "super"키워드 사용)

생성자 본문이 명시 적 생성자 호출로 시작하지 않고 선언되는 생성자가 기본 클래스 Object의 일부가 아닌 경우 생성자 본문은 다음의 생성자의 호출 인 수퍼 클래스 생성자 호출 "super ();"로 암시 적으로 시작됩니다. 인수를 취하지 않는 직접 수퍼 클래스. 등등 .. Object의 생성자까지 다시 호출되는 생성자의 전체 체인이있을 것입니다. "Java 플랫폼의 모든 클래스는 객체의 자손입니다". 이것을 " 생성자 체인 "이라고합니다.

이제 왜 그렇습니까?
그리고 Java가 이러한 방식으로 ConstructorBody를 정의한 이유는 객체 의 계층 유지 해야하기 때문입니다. 상속의 정의를 기억하십시오. 클래스를 확장하고 있습니다. 그렇게 말하면 존재하지 않는 것을 확장 할 수 없습니다. 기본 (수퍼 클래스)을 먼저 생성 한 다음 파생 (하위 클래스) 할 수 있습니다. 그래서 그들은 부모와 자식 클래스라고 불렀습니다. 부모없이 아이를 가질 수 없습니다.

기술적 인 수준에서 하위 클래스는 부모로부터 모든 멤버 (필드, 메서드, 중첩 클래스)를 상속합니다. 그리고 생성자는 멤버가 아니기 때문에 (객체에 속하지 않습니다. 객체 생성을 담당합니다) 서브 클래스에 상속되지는 않지만 호출 될 수 있습니다. 그리고 객체 생성시 하나의 생성자 만 실행 되기 때문에. 그렇다면 서브 클래스 객체를 생성 할 때 슈퍼 클래스 생성을 어떻게 보장할까요? 따라서 "생성자 연결"의 개념; 따라서 현재 생성자 내에서 다른 생성자 (예 : 수퍼)를 호출 할 수 있습니다. 그리고 Java는 계층 구조를 유지하고 보장하기 위해이 호출이 하위 클래스 생성자의 첫 번째 줄이되어야했습니다. 그들은 부모 객체를 FIRST로 명시 적으로 생성하지 않으면 (잊은 경우처럼) 암시 적으로 수행 할 것이라고 가정합니다.

이 검사는 컴파일 중에 수행됩니다. 하지만 런타임에서 어떤 일이 발생하는지, 어떤 종류의 런타임 오류가 발생하는지 잘 모르겠습니다. 자바가 중간에있는 하위 클래스의 생성자 내에서 기본 생성자를 실행하려고 할 때 Java가 컴파일 오류를 발생시키지 않으면 첫 번째 줄이 아니라 몸매 ...


당신은 이유를 물었고, 다른 대답 인 imo는 슈퍼의 생성자를 호출하는 것이 왜 괜찮은지 말하지 않았지만 그것이 첫 번째 줄인 경우에만 그렇습니다. 그 이유는 실제로 생성자를 호출 하지 않기 때문입니다 . C ++에서 동등한 구문은 다음과 같습니다.

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

이니셜 라이저 절 자체를 보면 여는 중괄호 앞에 그것이 특별하다는 것을 알 수 있습니다. 나머지 생성자가 실행되기 전과 실제로 멤버 변수가 초기화되기 전에 실행됩니다. Java는 그렇게 다르지 않습니다. 생성자가 실제로 시작되기 전에 하위 클래스의 멤버가 초기화되기 전에 일부 코드 (다른 생성자)를 실행할 수있는 방법이 있습니다. 그리고 그 방법은 super맨 첫 줄에 "call"(예 :)을 넣는 것 입니다. (어떤 의미에서 superor this는 첫 번째 여는 중괄호 앞의 것입니다. 모든 것이 완전히 생성되기 전에 실행되기 때문입니다.) 여는 중괄호 이후의 다른 코드 (예 :int c = a + b;) 컴파일러가 "오, 좋아요, 다른 생성자는 없습니다. 그러면 모든 것을 초기화 할 수 있습니다."라고 말합니다. 따라서 실행되어 슈퍼 클래스와 멤버 및 기타 항목을 초기화 한 다음 여는 중괄호 다음에 코드 실행을 시작합니다.

몇 줄 후에 "이 객체를 생성 할 때 여기에 기본 클래스의 생성자에 전달할 매개 변수가 있습니다."라는 코드를 만나면 너무 늦었고 그렇지 않습니다. 어떤 의미가 있습니다. 따라서 컴파일러 오류가 발생합니다.


따라서 super를 호출하기 전에 로직을 실행하는 것을 막지는 않습니다. 단일 표현식에 맞출 수없는 논리를 실행하는 것을 막을뿐입니다.

실제로 여러 가지 방법으로 논리를 실행할 수 있습니다. 코드를 정적 함수로 래핑하고 super 문에서 호출하기 만하면됩니다.

귀하의 예를 사용하여 :

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}

전적으로 동의합니다. 제한이 너무 강력합니다. 정적 도우미 메서드 (Tom Hawtin-tackline이 제안한대로)를 사용하거나 모든 "pre-super () 계산"을 매개 변수의 단일 표현식으로 밀어 넣는 것이 항상 가능한 것은 아닙니다. 예 :

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

Carson Myers가 제안한 것처럼 "아직 생성되지 않은 개체"예외를 사용하면 도움이 될 수 있지만 각 개체 생성 중에이를 확인하면 실행 속도가 느려집니다. 언어 사양이 복잡하더라도 결과적으로 if 문을 금지하지만 매개 변수 내에서?-연산자를 허용하는 대신에 더 나은 차별화를 만드는 Java 컴파일러를 선호합니다.


나는 woraround를 찾았습니다.

이것은 컴파일되지 않습니다.

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
        doSomething(c);
        doSomething2(a);
        doSomething3(b);
    }
}

이것은 작동합니다.

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        this(a + b);
        doSomething2(a);
        doSomething3(b);
    }

    private MySubClass(int c) {
        super(c);
        doSomething(c);
    }
}

제 생각에는 Java 코드를 처리하는 도구를 작성하는 사람들과 Java 코드를 읽는 사람들이 어느 정도 삶을 더 쉽게 만들기 위해이 작업을 수행 한 것 같습니다.

super()또는 this()호출이 이동 하도록 허용하면 더 많은 변형을 확인할 수 있습니다. 예를 들어 당신이 이동하는 경우 super()또는 this()조건부로 전화 if()가 암시 삽입하는 스마트 충분해야 할 수도 있습니다 super()로를 else. 그것은 당신이 호출하면 오류를보고하는 방법을 알고해야 super()두 번, 또는 사용 super()this()함께. super()또는 this()호출 될 때까지 수신자에 대한 메서드 호출을 허용하지 않고 그것이 언제 복잡해 지는지 파악 해야 할 수 있습니다 .

모든 사람이이 추가 작업을 수행하도록하는 것은 아마도 이익보다 더 큰 비용처럼 보였습니다.


컴파일러에 이러한 제한이 없다면 나쁜 일이 발생하는 코드 예제를 제공 할 수 있습니까?

class Good {
    int essential1;
    int essential2;

    Good(int n) {
        if (n > 100)
            throw new IllegalArgumentException("n is too large!");
        essential1 = 1 / n;
        essential2 = n + 2;
    }
}

class Bad extends Good {
    Bad(int n) {
        try {
            super(n);
        } catch (Exception e) {
            // Exception is ignored
        }
    }

    public static void main(String[] args) {
        Bad b = new Bad(0);
//        b = new Bad(101);
        System.out.println(b.essential1 + b.essential2);
    }
}

생성 중 예외는 거의 항상 생성중인 객체를 제대로 초기화 할 수없고 현재 상태가 좋지 않고 사용할 수 없으며 가비지 수집되어야 함을 나타냅니다. 그러나 서브 클래스의 생성자는 수퍼 클래스 중 하나에서 발생한 예외를 무시하고 부분적으로 초기화 된 객체를 반환하는 기능이 있습니다. 위의 예에서 주어진 인수 new Bad()가 0이거나 100보다 크면 둘 다 제대로 초기화 되지 essential1않습니다 essential2.

예외를 무시하는 것은 항상 나쁜 생각이라고 말할 수 있습니다. 좋아요, 여기 또 다른 예가 있습니다 :

class Bad extends Good {
    Bad(int n) {
        for (int i = 0; i < n; i++)
            super(i);
    }
}

재미 있지 않나요? 이 예에서는 몇 개의 개체를 만들고 있습니까? 하나? 두? 아니면 아무것도 ...

호출을 허용 super()하거나 this()생성자 중간에 있으면 Pandora의 흉악한 생성자 상자가 열립니다.


반면에, 나는를 호출하기 전에 약간의 정적 인 부분을 포함하는 자주의 필요성을 이해 super()또는 this(). 이것은 this참조에 의존하지 않고 (사실 생성자의 맨 처음에 이미 존재하지만 super()또는 this()반환 될 때까지 순서대로 사용할 수없는 ) 이러한 호출을 수행하는 데 필요한 코드 일 수 있습니다. 또한 다른 메서드와 마찬가지로 호출 전에 일부 지역 변수가 생성 super()되거나 this()이후에 필요할 가능성이 있습니다.

이러한 경우 다음과 같은 기회가 있습니다.

  1. 이 답변에 제시된 패턴을 사용하여 제한을 피할 수 있습니다.
  2. Java 팀이 사전 super()및 사전 this()코드 를 허용 할 때까지 기다리십시오 . 생성자에서 발생 super()하거나 this()발생할 수있는 위치에 제한을 적용하여 수행 할 수 있습니다. 실제로 오늘날의 컴파일러조차도 생성자의 시작 부분에 정적 코드 추가를 안전하게 허용 할 수있을만큼 충분한 정도로 좋은 사례와 나쁜 경우 (또는 잠재적으로 나쁜 경우)를 구분할 수 있습니다. 실제로, 가정 super()this()반환 this차례로, 생성자가있다, 참조 및

return this;

끝에. 뿐만 아니라 컴파일러는 코드를 거부합니다.

public int get() {
    int x;
    for (int i = 0; i < 10; i++)
        x = i;
    return x;
}

public int get(int y) {
    int x;
    if (y > 0)
        x = y;
    return x;
}

public int get(boolean b) {
    int x;
    try {
        x = 1;
    } catch (Exception e) {
    }
    return x;
}

"변수 x가 초기화되지 않았을 수 있습니다"라는 오류가 발생하면 this변수에서 그렇게 할 수 있으며 다른 지역 변수와 마찬가지로 검사를 수행 할 수 있습니다. 유일한 차이점은 또는 호출 this이외의 다른 방법으로 할당 할 수 없으며 (일반적으로 생성자에 이러한 호출이없는 경우 처음에 컴파일러에 의해 암시 적으로 삽입 됨) 두 번 할당되지 않을 수 있습니다. 의심스러운 경우 ( 실제로 항상 할당 되는 첫 번째에서와 같이 ) 컴파일러는 오류를 반환 할 수 있습니다. 또는 앞에 주석이없는 생성자에 대해 단순히 오류를 반환하는 것보다 낫습니다 .super()this()super()get()xsuper()this()


익명 초기화 블록을 사용하여 생성자를 호출하기 전에 자식의 필드를 초기화 할 수 있습니다. 이 예는 다음을 보여줍니다.

public class Test {
    public static void main(String[] args) {
        new Child();
    }
}

class Parent {
    public Parent() {
        System.out.println("In parent");
    }
}

class Child extends Parent {

    {
        System.out.println("In initializer");
    }

    public Child() {
        super();
        System.out.println("In child");
    }
}

그러면 다음이 출력됩니다.

In parent
In initializer
In child


생성자가 파생 순서대로 실행을 완료하는 것이 합리적입니다. 수퍼 클래스는 서브 클래스에 대한 지식이 없기 때문에 수행해야하는 초기화는 서브 클래스가 수행하는 초기화와는 별개이며 가능한 전제 조건입니다. 따라서 먼저 실행을 완료해야합니다.

간단한 데모 :

class A {
    A() {
        System.out.println("Inside A's constructor.");
    }
}

class B extends A {
    B() {
        System.out.println("Inside B's constructor.");
    }
}

class C extends B {
    C() {
        System.out.println("Inside C's constructor.");
    }
}

class CallingCons {
    public static void main(String args[]) {
        C c = new C();
    }
}

이 프로그램의 출력은 다음과 같습니다.

Inside A's constructor
Inside B's constructor
Inside C's constructor

나는 내가 파티에 조금 늦었다는 것을 알고 있지만, 나는이 트릭을 몇 번 사용했다 (그리고 나는 그것이 조금 특이하다는 것을 안다) :

InfoRunnable<T>한 가지 방법으로 일반 인터페이스 만듭니다 .

public T run(Object... args);

그리고 생성자에 전달하기 전에 뭔가를해야한다면 다음과 같이합니다.

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));

실제로 super()는 생성자의 첫 번째 명령문입니다. 왜냐하면 서브 클래스가 생성되기 전에 수퍼 클래스가 완전히 형성되었는지 확인하기 때문입니다. super()첫 번째 명령문에 없는 경우에도 컴파일러가이를 추가합니다!


생성자가 다른 생성자에 의존하기 때문입니다. 생성자가 올바르게 작동하려면 다른 생성자에 필요한 것이 올바르게 작동합니다. 이것이 생성자에서 this () 또는 super ()에 의해 호출 된 종속 생성자를 먼저 확인해야하는 이유입니다. this () 또는 super ()에 의해 호출 된 다른 생성자에 문제가 있으면 호출 된 생성자가 실패하면 모두 실패하므로 다른 명령문을 실행합니다.


Tldr :

다른 답변은 질문의 "이유"를 다루었습니다. 이 제한에 대한 해킹을 제공하겠습니다 .

기본 아이디어는 포함 된 진술로 진술 탈취 하는 super것입니다. 이것은 당신의 진술을 표현 으로 위장함으로써 가능합니다 .

Tsdr :

우리가 전화하기 전에 우리가 할 일 Statement1()고려하십시오 .Statement9()super()

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        super(_1, _2, _3); // compiler rejects because this is not the first line
    }
}

물론 컴파일러는 우리 코드를 거부합니다. 따라서 대신 다음을 수행 할 수 있습니다.

// This compiles fine:

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        super(F(_1), _2, _3);
    }

    public static T1 F(T1 _1) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        return _1;
    }
}

유일한 제한은 부모 클래스가 적어도 하나의 인수를받는 생성자를 가져야 우리의 진술을 표현식으로 몰래 들어갈 수 있다는 것입니다.

다음은보다 정교한 예입니다.

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        i = i * 10 - 123;
        if (s.length() > i) {
            s = "This is substr s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        Object obj = Static_Class.A_Static_Method(i, s, t1);
        super(obj, i, "some argument", s, t1, t2); // compiler rejects because this is not the first line
    }
}

재 작업 :

// This compiles fine:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        super(Arg1(i, s, t1), Arg2(i), "some argument", Arg4(i, s), t1, Arg6(i, t1));
    }

    private static Object Arg1(int i, String s, T1 t1) {
        i = Arg2(i);
        s = Arg4(s);
        return Static_Class.A_Static_Method(i, s, t1);
    }

    private static int Arg2(int i) {
        i = i * 10 - 123;
        return i;
    }

    private static String Arg4(int i, String s) {
        i = Arg2(i);
        if (s.length() > i) {
            s = "This is sub s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        return s;
    }

    private static T2 Arg6(int i, T1 t1) {
        i = Arg2(i);
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        return t2;
    }
}

사실, 컴파일러는 우리를 위해이 프로세스를 자동화 할 수있었습니다. 그들은하지 않기로 선택했습니다.


자식 개체를 생성하려면 먼저 부모 개체를 만들어야합니다. 다음과 같이 클래스를 작성할 때 아시다시피 :

public MyClass {
        public MyClass(String someArg) {
                System.out.println(someArg);
        }
}

다음으로 바뀝니다 (extend와 super는 숨겨져 있습니다) :

public MyClass extends Object{
        public MyClass(String someArg) {
                super();
                System.out.println(someArg);
        }
}

먼저 Object를 만들고이 개체를 MyClass. MyClass이전에 만들 수 없습니다 Object. 간단한 규칙은 부모의 생성자가 자식 생성자보다 먼저 호출되어야한다는 것입니다. 그러나 우리는 클래스가 하나 이상의 생성자를 가질 수 있다는 것을 알고 있습니다. Java를 사용하면 호출 될 생성자를 선택할 수 있습니다 ( super()또는 super(yourArgs...)). 따라서 super(yourArgs...)작성할 때 부모 개체를 만들기 위해 호출 될 생성자를 재정의합니다. super()객체가 아직 존재하지 않기 때문에 이전 에는 다른 메소드를 실행할 수 없습니다 (그러나 super()객체가 생성되고 원하는 작업을 수행 할 수있게됩니다).

그렇다면 왜 우리는 this()어떤 방법으로도 실행할 수 없습니까? 아시다시피 this()현재 클래스의 생성자입니다. 또한 클래스에 다른 수의 생성자를 가질 수 있으며 this()또는 처럼 호출 할 수 this(yourArgs...)있습니다. 내가 말했듯이 모든 생성자에는 숨겨진 메서드가 super()있습니다. 우리는 우리의 정의를 쓸 때 super(yourArgs...)우리는 제거 super()와 함께 super(yourArgs...). 또한 우리는 정의 할 때 this()또는 this(yourArgs...)우리는 또한 우리를 제거 super()하는 경우 때문에 현재의 생성자에서 super()함께 있던 this()같은 방법으로, 더 후, 하나의 상위 개체를 만드는 것입니다. 그것이 this()방법 에 대해 동일한 규칙이 부과 된 이유 입니다. 부모 객체 생성을 다른 자식 생성자로 재전송하고 해당 생성자는super()부모 생성을위한 생성자. 따라서 코드는 실제로 다음과 같습니다.

public MyClass extends Object{
        public MyClass(int a) {
                super();
                System.out.println(a);
        }
        public MyClass(int a, int b) {
                this(a);
                System.out.println(b);
        }
}

다른 사람들이 말했듯이 다음과 같은 코드를 실행할 수 있습니다.

this(a+b);

또한 다음과 같은 코드를 실행할 수 있습니다.

public MyClass(int a, SomeObject someObject) {
    this(someObject.add(a+5));
}

그러나 메서드가 아직 존재하지 않기 때문에 다음과 같은 코드를 실행할 수 없습니다.

public MyClass extends Object{
    public MyClass(int a) {

    }
    public MyClass(int a, int b) {
        this(add(a, b));
    }
    public int add(int a, int b){
        return a+b;
    }
}

또한 메서드 super()체인에 생성자 가 있어야 this()합니다. 다음과 같은 개체를 만들 수 없습니다.

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}

class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

생성자 C(int x)호출하는 경우 예제를 참조하십시오. C()첫 번째 줄에서 호출하지 않으면 z의 값이 y에 따라 달라지며 z 의 문제가됩니다. z는 올바른 값을 얻을 수 없습니다.

참고 URL : https://stackoverflow.com/questions/1168345/why-do-this-and-super-have-to-be-the-first-statement-in-a-constructor

반응형