class B extends A { ... } 와 같은 상속 관계가 성립되면,
B is A 이고, A is not B의 관계로 볼 수 있다.
즉, B는 A보다 작기 때문에 B가 A에 들어갈 수 있는 관계로 정의된다.
위와 같이 B가 A를 상속한 상태에서,
A a = new A(); // A에 당연히 A객체가 들어갈 수 있다. B b = new B(); // B에 당연히 B객체가 들어갈 수 있다. A a = new B(); // 알아서 B 중 A와 겹치는(?) 부분만 접근 가능한 형태로 들어간다. = 업캐스팅(?)
즉 자식은 부모 타입에 들어가진다. 물론 자식의 디테일한 변경사항까지 접근할수는 없지만, 적어도 부모로부터 상속받은 속성과 메서드는 접근할 수 있다.
(참고로 업캐스팅이라는 개념은 없다)
바로 여기에서 다형성(Polymorphism)이라는 특성이 나타난다.
하나의 부모로부터 상속받은 자식들은 부모 타입안에 담길 수 있기 때문에, 특정 메서드의 파라메터로 부모형을 선언해두면, 여기에 다양한 자식 객체를 argument로 받아서 처리가 가능해지는 것이다!!
그러나 B b = new A(); 는 불가능하다. A가 부모이기 때문에 더 넓은 개념이 더 좁은 개념에 들어갈 수 없다고 볼 수 있다. 이때는 아래와 같이 오브젝트 캐스팅(= 다운 캐스팅)이 필요하다.
B b = (B) new A(); // 부모에서 자녀로 아래 방향이므로 다운 캐스팅
다운 캐스팅의 예를 보여주기 위해 위와 같이 부모 객체를 생성해서 다운 캐스팅을 했지만, 문법적으로는 맞을지 몰라도 실제로는 오류가 난다.
위와 같이 부모를 자녀로 한것이라면 자녀에만 독자적으로 있는 것에는 접근할 경우 오류가 발생하는 것이다.
일반적으로 다운 캐스팅은 앞서 이야기한 다형성을 사용해서 자식 객체를 부모형으로 argument로 받아온 후에 자식 객체의 고유한 속성과 메서드에 접근하기 위해 다시 자식형으로 바꾸어주기 위해 사용한다. 이렇게 원래는 B였으나 A로 업캐스팅(?)된 것을 다시 다운캐스팅하면 B에 독자적으로 있는 것에도 접근가능하다.
왜 부모가 더 큰가?
사실 B가 기능은 더 많아져서 큰 것처럼 보이지만, 형으로 생각한다면 더 세부적이고 좁아지는 것으로 볼 수 있다.
이런 의미에서 부모가 더 크다고 이야기할 수 있다.
super 키워드의 기능
① super.변수명 으로 부모클래스의 전역변수를 호출할 수 있다. (부모와 자식의 변수명이 동일한 경우..)
만약 부모에만 B라는 변수가 있다면,
B로 불러도 되고,
this.B로 불러도 되고,
super.B로 불러도 된다!
만약 자녀에만 C라는 변수가 있다면,
C로 불러도 되고,
this.C로 불러도 되지만,
super.C로 부르면 에러난다!!!
② super.method명( 파라메터 ); 으로 자식클래스에서 부모클래스의 메소드 호출가능.
오버라이딩한 메소드 aaa()가 있다면,
aaa()로 호출하면 자식클래스의 것이 실행되고,
this.aaa()로 호출해도 자식클래스의 것이 실행되지만,
super.bbb() 는 부모클래스의 것이 실행된다.
★외부에서 호출할때, 재정의된 메소드는 무조건 자식의 것이 호출된다.
변수의 경우 업캐스팅하면 자식의 것에 접근이 안되지만, 업캐스팅했다하더라도 메소드가 재정되었다면 재정의된 메소드가 호출된다.
단, 재정의되지 않은 메소드가 자식에만 있다면, 업캐스팅한 상태에서 자식에만 있는 메소드는 접근할 수 없다.
★자식클래스 객체에 부모 객체를 다운캐스팅하여 할당하면, 컴파일은 되지만, 실행할 때 ClassCastException 에러가 발생한다.
자식 objName = (자식) 부모객체; //문법적으로 틀리진 않았지만 실행시에 문제됨.
③ super( 파라메터..)로 부모의 생성자를 호출가능 ( 자식 클래스 생성자의 첫줄에.. )
class SubClass extends SuperClass { SubClass() { super(); // 부모클래스의 기본생성자 호출.. 생략해도 실행된다. 무조건 생성자 첫째줄!!
상속 시의 생성자 호출 순서
자식 클래스가 생성될 때 부모 클래스의 기본 생성자가 먼저 호출된다.
그리고 나서 자식 생성자도 실행된다.
super(); //쓰지 않아도 생략해도 기본적으로 실행된다.
만약 부모클래스의 기본 생성자가 존재하지 않을 때에 다른 생성자가 존재하면
이를 상속하는 자식클래스에서 오류가 일어나게 된다.
자식 클래스가 생성될 때무조건 부모클래스의 기본 생성자를 호출하게 되기 때문이다.
따라서 상속을 고려해서 기본 생성자를 비어있더라도 생성한 후 오버로딩하여 사용한다.
즉, 부모가 하나라도 생성자를 만든다면 반드시 디폴트 생성자도 하나 만들어주는게 좋다.
혹은 super( 파라메터 ); 등으로 자식클래스 생성자 첫줄에서 먼저 호출해준다면 비어있는 기본 생성자가 없다고 해서 오류가 나지 않는다.