-
Notifications
You must be signed in to change notification settings - Fork 1
Day 14(21.09.23)
객체 지향 프로그래밍에서의 상속이란 개념은 부모 클래스의 멤버(field, method)를 자식 클래스에게 물려주는 것을 뜻한다.
좀 더 이해하기 쉽게 얘기하면, 자동차 시장에는 포드, 페라리, 벤츠 등 다양한 메이커가 있고 메이커에 따라 성능과 외형은 천차만별이다. 하지만, 결국 자동차라는 큰 집합에 소속되어 있으며, 자동차라는 큰 개념이 가지는 공통적인 속성은 모든 자동차 메이커가 공통적으로 가지고 있다. 여기서 자동차라는 더욱 포괄적인 개념을 부모 클래스, 그리고 각 자동차 메이커를 자식 클래스라고 생각하면 좋다. 이러한 상속은 잘 개발된 클래스를 재사용하여 새로운 클래스를 만드는 것이므로 코드의 중복을 줄여주고, 더욱 효율적인 개발을 가능하게 한다.
하지만 객체 지향 프로그래밍에서의 상속은 현실과는 달리 자식 클래스가 부모 클래스를 선택할 수 있다. 자바에서는 이럴 때 extends라는 키워드를 사용한다. 예를 들어, Porsche의 경우 자동차 메이커로 유명하지만 한때 탱크와 같은 것들도 만들었던 브랜드이다. 하지만 자동차를 만들고 싶다 할때는 자동차 공장을 세우는 것처럼, 이 경우엔 Tank 클래스가 아닌 Car 클래스를 상속하는 것이다.
class Porsche extends Car {
//Field
//Constructor
//Method
}위 예시처럼 Porche는 extends 키워드를 사용해 Car 클래스를 상속하겠다고 선언하는 것이다. 하지만, 하나의 자동차 공장에서 탱크나 비행기를 뽑아낼 수는 없듯이(실제로는 가능할수도 있다고 쳐도) 자바 에서는 다중 상속을 허용하지 않고 있다. 즉, extends 키워드를 사용해서 Car 클래스를 상속하겠다고 선언했따면 Tank, Airplane과 같은 부모 클래스를 중복으로 가질 수 없다. 아래는 클래스 상속의 예시이다.
//Porche.java
public class Porsche {
public static void main(String[] args) {
Vehicle normalCar = new Vehicle();
SportsCar carrera = new SportsCar();
normalCar.maxSpeed = 120;
normalCar.name = "Just car";
normalCar.info();
carrera.maxSpeed = 300;
carrera.name = "Carrera";
carrera.cost = 12000;
carrera.info();
}
}//Vehicle.java
public class Vehicle {
int maxSpeed;
String name;
public void info() {
System.out.println("이름: " + name);
System.out.println("최대 속도: " + maxSpeed);
}
}//SportsCar.java
public class SportsCar extends Vehicle{
int cost;
}Result
이름 : Just car
최대 속도 : 120
이름 : Carrera
최대 속도 : 300
위 예시에서 확인할 수 있듯이 SportsCar 클래스는 부모 클래스인 Vehicle의 모든 필드와 생성자를 이어받았기 때문에 Porsche 클래스에서 name과 maxSpeed의 필드값을 설정할 수 있고, info() 메소드를 사용해서 정보를 불러오기까지 할 수 있다는 것을 확인했다. 하지만 여기서 문제가 있다. 분명 SportsCar 클래스에서는 cost라는 필드값을 하나 추가했는데 info() 메소드에 해당 내용이 반영되지 않았다. 이 부분은 메소드 재정의(Method Override)를 통해서 해결할 수 있다.
메소드 재정의란 부모 클래스의 메소드가 자식 클래스에 맞게 설계되어 있지 않아 자식 클래스에서 사용하기 어려울 경우, 문자 그대로 부모 클래스의 메소드를 '재정의(override)'함으로서 부모 클래스의 메소드를 자식 클래스의 상황에 맞게 사용할 수 있게된다. 여기서 메소드를 재정의 하기 위해선 '@Override'라는 어노테이션을 사용해야하는데, 예시는 아래와 같다.
//SportsCar.java
public class SportsCar extends Vehicle{
int cost;
@Override //Vehicle 클래스를 상속받은 SportsCar에서 부모 클래스의 info() 메소드를 재정의해서 사용하겠다고 선언
public void info() {
System.out.println("가격: " + cost);
}
}Result
이름: Just car
최대 속도: 120
가격: 12000
SportsCar 클래스에서 재정의(오버라이딩)한 info() 메소드가 제대로 적용된 것을 확인할 수 있다. 하지만 또 다시 문제가 발생했다. SportsCar의 필드값인 이름과 최대 속도가 보이질 않게 됐다. 여기서 메소드 재정의의 특징을 확인할 수 있는데, 자식 클래스에서 메소드를 재정의 하고, 자식 객체(여기선 SportsCar)에서 메소드를 호출하면 부모 메소드(여기선 Vehicle)가 호출되지 않고 자식 클래스의 info() 메소드만 호출되는 것을 알 수 있다. 이렇게 부모 클래스의 메소드를 호출해야하는 상황이 발생할 경우엔, 하나하나 출력 코드를 새로 작성할수도 있겠지만 부모 클래스의 메소드를 호출해서 사용한다면 훨씬 효율적으로 작업을 진행할 수 있게된다.
위 항목에서 설명했듯, 자식 클래스에서 부모 클래스의 메소드를 재정의 했고, 자식 객체에서 재정의한 메소드를 호출할 경우엔 부모 클래스의 메소드는 호출이 되지 않는다. 부모 클래스의 메소드가 필요가 없을땐 문제가 되지 않지만, 위 예시와 같이 부모 클래스의 메소드가 주는 정보(이름과 최대속도)가 필요한 경우에는 부모 클래스의 메소드를 호출해야 하는데, 이럴때 사용하는 것이 super 키워드이다. super를 명시적으로 사용해 부모 클래스의 생성자와 메소드 등을 호출할 수 있다.(이 페이지에서는 메소드만 호출한다.) 사용법은 아래와 같다.
//SportsCar.java
public class SportsCar extends Vehicle{
int cost;
@Override //Vehicle 클래스를 상속받은 SportsCar에서 부모 클래스의 info() 메소드를 재정의해서 사용하겠다고 선언
public void info() {
super.info(); //super키워드로 부모 클래스를 호출한 다음에 닷 연산자(.)로 info메소드를 호출
System.out.println("가격: " + cost);
}
}Result
이름: 그냥 차
최대 속도: 120
이름: Carrera
최대 속도: 300
가격: 12000
super 키워드를 사용한 다음에 부모 클래스의 info 메소드를 호출한 결과 의도했던 대로 이름과 최대 속도, 그리고 가격이 출력된 것을 확인할 수 있다.