오늘은 상속과 포함관계에 대해 정리해보고자 한다.
클래스들은 서로 관계를 맺어 다른 클래스에 있는 필드와 메서드를 가져와 코드작성을 더 쉽게 만들기도 하는데 이럴때 사용할 수 있는 방법이 상속과 포함이다.
상속은 B 클래스가 A 클래스의 종류에 속할 때, 포함은 B 클래스가 A 클래스의 구성요소 일 때 주로 사용한다.
상속관계 : is A
포함관계 : has A
상속은 한 클래스에서 다른 클래스로 필드와 메서드를 물려주기 위해서 사용한다.
이 때 물려주는 클래스는 부모 클래스, 물려받는 클래스는 자식 클래스로라 칭한다.
상속은 extends
키워드를 사용해서 정의할 수 있다.
접근제어자 class 자식클래스 extends 부모클래스 {
}
이 때, 자식 클래스는 부모클래스로부터 필드와 메서드를 모두 내려받아 사용가능할 수 있게 되고, 여기에 덧붙여 새로운 필드와 메서드를 더해 나가니 자식 클래스는 항상 부모클래스보다 몸집이 크거나 같게 된다.
부모클래스의 변화는 자식클래스에게 변화를 주지만, 자식클래스의 변화는 부모클래스에게 변화를 주지 않는다.
자식클래스가 매개변수를 자신만의 필드에 저장할 때는 this.
를 사용해 경로를 지정해줄 수 있다.
하지만 부모의 필드에 선언되어있어 굳이 자식클래스에 적어두지 않은 필드에 매개변수를 저장하고 싶을 때가 있을 것이다.
이 때, 받은 매개변수를 부모의 필드에 저장하고 싶다면 super.
을 사용하여 상위 클래스에 접근할 수 있다.
접근제어자 자식클래스명 ( 자식매개변수1, 자식매개변수2, ...) {
super.부모필드명 = 자식매개변수1;
부모의 생성자에 이미 같은 매개변수를 필드에 저장하는 구문이 쓰여있다면 똑같이 이 매개변수를 부모 생성자의 매개변수 자리에 넣어주고 싶을 것이다.
이 때 사용하는 것이 super()
이다.
super()
는 상위 클래스의 생성자를 호출한다.
접근제어자 자식클래스명 ( 자식매개변수1, 자식매개변수2, ... ) {
super ( 자식매개변수1, 자식매개변수2 )
}
이렇게 되면 부모의 매개변수 자리에 저 소괄호 안의 매개변수들이 같은 자리에 똑같이 들어간다.
*따라서 반드시 일치시킬 매개변수의 개수와 순서가 같아야한다.
예시)
부모 클래스가 아래와 같이 설정되어있다고 해보자.
public class Mom{
//필드
String eyecolor;
String haircolor;
boolean freckles;
String eyebrow = straight;
//생성자
public Mom (String eye, String hair, boolean freckles){
this.eyecolor = eye;
this.haircolor = hair;
this.freckles = freckles;
}
}
눈썹이 일자형
인 속성을 가지고 있고 눈 색
, 머리색
, 주근깨 유무
를 매개변수로 입력해주어야 객체가 생성되는 클래스이다.
자식클래스도 같은 필드들을 가지게 되지만 어처피 상속받게 되니 필드에 굳이 적지 않을것이다. 대신 super
문을 사용해서 매개변수들을 부모필드로부터 상속받은 필드들에 넣어줄 수 있다.
public class Child extends Mom {
//필드
int age;
//생성자
public Child(String 눈색깔, String 머리색깔, boolean 주근깨유무, String 눈썹타입){
super(눈색깔, 머리색깔, 주근깨유무);
super.eyebrow = 눈썹타입;
age=14;
}
}
자식생성자의 매개변수 눈색깔
, 머리색깔
, 주근깨유무
는 그대로 부모 클래스의 생성자 매개변수 자리 eye
, hair
, freckles
로 들어간다.
(
이 때 eyebrow
처럼 부모로부터 상속받은 필드는 가만히 두면 부모의 straight
값을 상속받았을테지만 super.
를 이용해서 필드에 접근해 수정을 해줄 수 있다.
그리고 두 가지를 같이 사용할 경우 super()
은 상위클래스의 생성자를 호출하는 것이기때문에 가장 위에 적힌다.
객체 생성시 자동형변환을 이용하여 자식클래스로 부모클래스 객체를 만드는 것도 가능하다.
Car SUV = new HyundaiCar();
예를 들어 Car
로부터 상속받는 HyundaiCar
클래스가 있다고 쳐보자.
HyundaiCar
클래스는 이미 Car
클래스의 필드와 메서드를 상속받아 다 가지고 있기 때문에 HyundaiCar
클래스로 객체를 만들어도 Car
클래스의 객체로서 작동하는 데에는 문제가 없다.
그러나, 이렇게 생성한 Car
객체는 HyundaiCar
의 필드나 메서드를 사용할 수 없다.
- 하지만 예외는 있다.
바로 자식클래스의 메서드가 부모객체로부터 상속받아 부모 메서드와 이름이 같은 경우이다.
이 상속받은 클래스가 자식 클래스에 와서 수정되었더라도
객체의 메서드를 실행하기 위해 메서드 정의를 찾아갈 때, 객체를 생성한 가장 아래의(구체적인) 클래스부터 거슬러 올라가며 확인하기 때문이다.
부모 클래스로부터 메서드를 상속받았지만 내용을 수정하고 싶을 때가 있는데,
이 때, 상속받은 메서드의 내용을 재정의 하는 것을 오버라이딩이라고 한다.
객체의 메서드를 실행할 때, 해당 객체를 만든 가장 자식의 클래스의 메서드부터 확인을 하기 때문에 자식메서드와 부모메서드가 같은 이름을 가지고 있더라도 자식 클래스는 자신의 메서드를 실행시킬 수 있다.
메서드의 내용을 재정의할 때 메서드의 위에 @Override
를 적어줌으로써 이 메서드가 부모의 메서드로부터 상속받은 메서드였음을 명시해줄 수 있다.