객체 지향의 상속
- 부모 클래스에서 만들어진 필드와 메소드를 자식 클래스가 물려 받는 것을 뜻한다.
- 따라서 부모 클래스에서 가지고 있는 필드와 메소드를 자식 클래스에서 만들 필요가 없어져 간결해진다.
만약 상속이 없었다면 위처럼 필드와 메소드가 중복되는 4개의 클래스를 각각 만들었어야 했을 것이다.
하지만 상속을 통해서 부모 클래스에 공통 필드, 메소드를 만들고, 나머지는 자식 클래스에서 필요한 것들을 상속받는다.
객체 지향에서 상속의 장점
- 자식 클래스의 간결화
- 계층 구조가 되어 클래스를 관리하기가 용이하다.
- 공통되는 메소드를 상속만 받아서 바로 사용할 수 있기 때문에 생산성이 향상된다.
클래스 상속 선언
extends를 통해서 person이라는 부모클래스를 student라는 자식 클래스가 상속받게 된다.
또 studentwoker은 student 클래스에서 상속받는다.
<예제>
상속의 특징
다중 상속은 불가능하다. 우리는 상속할 때 extends를 쓰는데, 이 뒤에는 클래스가 온다. 이 때 class, class이런 식으로 불가능하다는 뜻이다. 즉, 자식 클래스가 부모 클래스 여러개를 한번에 상속받는 것이 불가능하다는 것이다.
대신, 부모가 자식을 여러개 거느리는 것은 가능하다.
상속 횟수에는 제한이 없다. 이 횟수는 부모가 자식을 여럿 두는 횟수를 말한다.
자바에서 최상위 조상 클래스는 java.lang.Object다. 우리가 이를 따로 선언하지는 않는데, 컴파일러가 자동으로 처리한다.
서브 클래스/슈퍼 클래스의 생성자 호출 및 실행
1. 서브 클래스가 슈퍼 클래스를 상속 받은 것인데, 이 때 서브 클래스의 생성자뿐 아니라 슈퍼 클래스의 생성자도 호출한다.
2. 슈퍼 클래스의 생성자가 먼저 실행되고, 그 이후에 서브 클래스의 생성자가 실행된다.
jvm은 먼저 가장 아래의 public 클래스를 찾아간다. 여기서 C클래스 타입의 c객체를 생성한다. 이 때 3번째의 생성자를 호출하러 올라간다. 여기서 실행하려고 보니 이 C클래스는 B의 상속을 받은 자식 클래스라서 이 클래스의 부모 클래스 생성자인 B클래스로 올라간다. 여기서도 부모 클래스가 존재해서 결국 A클래스까지 올라가게 된다. 이제 여기서부터 생성자 A를 실행하고 차례로 아래로 내려가면서 실행되게 된다. 이러한 과정으로 출력은 A, B, C가 나오게 되는 것이다.
서브 클래스에서 슈퍼 클래스의 생성자 선택
- 여러 생성자를 작성하는 것이 가능하다.
- 생성자를 하나 선택해서 찾아가고, 이는 컴파일러가 자동으로 실행한다.
- 여러 생성자가 있을 때 선택하는 것은 super()를 이용한다.
슈퍼 클래스의 기본 생성자가 자동 선택
위의 그림과 비슷하다. 메인에서 객체 생성한다고 B를 호출하고, B에서는 A클래스를 호출한다. 이 때 A클래스에는 생성자가 2개가 있는데, 컴파일러가 자동으로 기본 생성자를 찾아간다.
슈퍼 클래스에서 기본 생성자가 없어 오류 난 경우
앞에서도 계속 배웠지만, 매개변수가 있는 생성자가 이미 있을 경우 자동으로 기본 생성자가 만들어지지 않는다. 따라서 기본 생성자를 찾아가지 못해 오류가 나게 되는 것이다.
서브 클래스에서 매개변수를 가진 생성자
객체를 만들 때 매개변수가 있는 것을 생성한다. 따라서 컴파일러가 매개변수가 있는 생성자를 찾아가게 된다. B에서는 부모 A를 찾아가고 여기서는 부모가 없기 때문에 기본 생성자에서 다음으로 B로 가서 매개변수가 있는 생성자를 호출한다.
이때 B생성자에서 A생성자로 갈 때 기본 생성자를 간 이유는 객체에서 생성자를 찾아갈 때와 생성자가 생성자를 찾아가는 것이 다르기 때문이다. 자식 생성자가 부모 생성자를 찾아갈 대는 매개변수와 상관없이 기본 생성자를 찾아간다. 생성자에서 생성자를 찾아갈 때는 매개변수는 상관이 없다.
*다른 생성자를 찾아가고자 할 때는 아래의 방법이 있다.
super()를 이용해 명시적으로 슈퍼 클래스 생성자 선택
- 인자를 선택해서 적당한 생성자를 골라 호출한다.
- super()은 this처럼 첫번째 라인에 적어야 한다.
메인에서 객체를 생성해 매개변수가 1개 있는 B클래스의 생성자를 호출한다. 이 생성자는 서브 클래스라서 부모 클래스 생성자를 호출하러 가는데, 이 때 super()가 있다. 생성자를 호출하기 전에 이 super을 먼저 확인한다. 메인에서 x에 5를 넣었기 때문에 매개변수 5를 가지고 A클래스에서 매개변수가 있는 생성자 A를 찾아간다.
이후에는 x에 들어간 값에 따라 결과를 출력한다.
<예제>
15번에서 16가기 전에 먼저 5번 찾아감(super없어서 기본 생성자로) 그래서 8번에는 기본 생성자의 값인 0,0이 들어가게 되고, 0,0이 출력된다. 이 때, 5번이 아니라 6번으로 가게 하는 것이 super()이다.
super()을 사용해서 매개변수 2개짜리 생성자로 찾아가도록 했고, 5,6이 출력되었따.
업캐스팅
서브클래스 객체는 슈퍼클래스로부터 상속된 멤버와 자신의 멤버를 가지고 있다. 그리고 이 서브클래스의 객체를 슈퍼클래스의 참조변수로 가리킬 수 있다.
위 로직에서 서브클래스의 객체를 생성한 이후 빨간 글을 보면, 학생 클래스의 객체 s를 슈퍼클래스의 참조변수 p가 가리키고 있는 것을 볼 수 있다.
이처럼 서브클래스의 객체를 슈퍼클래스의 참조변수가 가리킬 수 있음을 알 수 있다.
이렇게 업캐스팅된 p는 슈퍼클래스의 멤버만 접근할 수 있다.
즉 'p.슈퍼클래스멤버' 가 되는 것이다.
메인 메소드를 보면 슈퍼클래스 p를 선언하고, 학생 클래스의 s객체를 생성한다. 이때 '이재문'이라는 값이 주어졌다. 이 객체를 생성하기 위해서 학생 생성자에 가서 super를 보고 매개변수가 있는 사람 생성자로 간다. 그리고 여기에 있는 매개변수 name에 가져왔던 매개변수 '이재문'을 넣는다. 그리고 this.를 통해서 사람 클래스의 name에 '이재문'이 들어가게 된다. 마지막으로 p.name을 하게되면 '이재문'이 출력된다.
여기서 p는 오버캐스팅 한 것으로 오른쪽 그림의 분홍색은 업캐스팅 된 p에 접근 가능한 멤버들이다.(슈퍼클래스의 멤버들)
다운캐스팅
여기서 p는 슈퍼클래스인데, 이를 s객체를 가르키도록 다운캐스팅했다. 이를 통해서 서브클래스의 객체 s는 슈퍼클래스와 서브클래스의 멤버 모두에게 접근 가능해진다.
업캐스팅 레퍼런스가 가리키는 객체
1.
사람이 슈퍼클래스, 학생과 조사원이 서브클래스 / 조사원이 슈퍼클래스, 교수가 서브클래스
2.
소스가 4개지만 모두 p로 받았다. 여기서 업캐스팅은 첫번째를 제외한 모든 경우로 총 3번이 일어났다
3.
p를 매개변수로 가지는 print메소드를호출하는데, 이 메소드는 매개변수가 Person의 person으로 되어있다. 여기서 Person은 클래스 타입, person은 참조변수다.(이때까지는 기본타입에 매개변수가 왔었음)
이 메소드에 p를 넘긴다. 이 p는 person으로 가게 되며, Person p가 된다. Person p는 총 4개가 있는데, 이 중 무엇을 가리키는 지 알기 혼란스럽다. 이때 사용하는 것이 instanceof다.
여기서 참과 거짓을 판단한다.
instanceof가 앞의 참조변수가 뒤의 객체에 속하는가와 타입인가를 판단한다.
jee는 두 경우가 모두 참이다.
kim은 교수타입 이라서 거짓, 참, 참이 된다. 리서처는 교수의 슈퍼클래스이기 때문이다.(업캐스팅)
lee는 person과 researcher만 되기 때문에 거짓이 된다.
<예제>
메소드 오버라이딩
- 메소드2를 서브 클래스에서 오버라이딩
- 왼쪽 메소드2 호출이 슈퍼클래스가 아니라 서브클래스로 가는 이유는 서브클래스로 오버라이딩 되어 있기 때문
- 동적 바인딩은 실행이 될 때 넘어간다
슈퍼 클래스의 draw가 서브 클래스에서 각각 오버라이딩되어 재구현.
위는 그냥 자기 객체를 생성한 것이고, 아래의 것은 업캐스팅이 실행된 것.
draw메소드가 위는 서브 클래스에 있는 것이고, 아래는 슈퍼 클래스에 있는 것이다.
위의 경우 서브 클래스이기 때문에 그냥 호출하면 끝.
아래의 경우 슈퍼 클래스로 갔다가 오버라이딩 되어 있기 때문에 서브 클래스로 내려오는데, 이 과정을 동적 바인딩이라고 한다.(컴파일 단계가 아닌 실행 단계에 이루어짐 - 반대로 오버로딩은 컴파일 단계(정적 바인딩))
<예제>
오버라이딩과 super 키워드
Shape b = new Circle();로 업캐스팅 하면서 객체 생성과 함께 생성자 호출
b.paint();는 paint메소드에서 그 아래로 갔다가 public void draw()로 가고, 그 이후로 오버라이딩 된 Circle클래스의 draw메소드로 간다. 이를 동적바인딩이라고 한다. 다음으로 name에 "Circle"을 넣는데 이는 아래에 있는 출력문에 들어가는 것이고, 그 아래의 super.name이 위의 출력문으로 간다. 즉, 슈퍼 클래스의 name으로 가서 출력되는 것이 "Shape"이고, 서브 클래스인 Circle에서 출력되는 것이 "Circle"이다.
super.으로 움직이는 것은 정적 바인딩으로 이 과정은 컴파일 과정에서 일어난다.
오버라이딩 vs 오버로딩
메소드 이름 : 오버라이딩은 상속 받은 메소드 이름이 같고, 오버로딩은 상속받지 않았는데 메소드 이름이 동일(생성자)
오버로딩 : 타입이나 갯수는 달라야 하지만 리턴 타입은 상관 없음
오버라이딩 : 타입, 갯수, 리턴 타입 모두 같아야 함
<예제>
'학원 > JAVA - 학원' 카테고리의 다른 글
내부 클래스, 무명 클래스 (0) | 2022.02.16 |
---|---|
(상속) 추상 클래스 / 추상 메소드 / 인터페이스 (0) | 2022.02.16 |
클래스와 객체 (0) | 2022.02.07 |
예외 처리(Exception) (0) | 2022.02.04 |
배열 (0) | 2022.02.01 |