📖클래스 간 상속관계에서 슈퍼클래스가 보유한 메서드를 서브클래스가 재정의하여 사용하는 프로그래밍 기법
Java의 클래스 간 상속관계에서 서브클래스는 슈퍼클래스의 private를 제외한 모든 멤버를 상속받는다. 이렇게 상속받은 멤버는 그대로 사용이 가능하며, 서브클래스만의 개성을 추가하여 사용할 수도 있다. 여기서 서브클래스만의 개성을 추가하여 메서드를 재정의하는 기법을 오버라이드(override)라고 하며, 사용하는 것을 오버라이딩(overridng)이라고 한다.
오버라이딩을 할 때 위와 같은 특징들을 고려하여 사용해야한다. 하지만 걱정할 필요는 없다. 규칙에 위반되면 에러가 발생할테니 말이다. 아래의 코드를 보자.
//바지 클래스 선언
public class Pants{
String size;
int price;
public Pants(String size, int price){
this.size = size;
this.price = price;
}
//int형 price를 반환하는 메서드 선언
public int getPrice(){
return price;
}
}
//바지를 상속받은 청바지 클래스 선언
public class Jean extends Pants{
//생성자 메서드 선언
public Jean(String size, int price){
super(size, price);
}
//int형 price를 String형으로 재정의
@override
public String getPrice(){ //컴파일 에러발생
return "35000";
}
}
위와 같이 오버라이딩은 반환형이 변경되면 컴파일 에러가 발생한다. 다음은 접근제한자가 슈퍼클래스의 메서드보다 접근범위가 좁은 경우를 보자.
//슈퍼클래스에서 public 메서드 선언
public int getPrice(){
return price;
}
//서브클래스에서 protected 메서드 선언
@override
protected int getPrice(){ //컴파일 에러발생
return price;
}
이러한 에러를 방지하기 위해 오버라이딩 시 선언부와 반환값은 변경해서는 안되며 오직 실행문에 대해서만 재정의하여 사용하면 된다. Java로 프로그래밍을 하다보면 수많은 상황에서 오버라이딩을 사용할 것이다.
오버라이딩에 대한 개념적인 설명 및 조건에 대해서 알아보았다. 슈퍼클래스의 메서드를 서브클래스에서 직접 정의하는 것을 알아보자.
//슈퍼클래스 정의
public class Top{
public void method(){
System.out.prinln("Top 메서드 입니다.");
}
}
//서브클래스 정의
public class Bottom extends Top{
@override
public void method(){
System.out.prinln("Bottom 메서드 입니다.");
}
}
//MethodTest클래스 정의
public class MethodTest{
public static void main(String[] args){
Top top = new Top();
top.method();
Bottom bottom = new Bottom();
bottom.method();
Top top = new Bottom(); //다형성 적용
top.method();
}
}
//실행결과
"Top 메서드 입니다."
"Bottom 메서드 입니다."
"Bottom 메서드 입니다."
위와 같이 세번째 결과가 Bottom 클래스의 메서드로 나온 것은 Java의 다형성이 적용되었기 때문이다. 다형성은 상속관계로 인해 하나의 클래스가 다양한 자료형으로 전환될 수 있는 특성을 의미한다. 자세한 이야기는 차후 다형성을 다룰 때 이야기 하도록 하자.
오버라이딩이 무엇인지 또 어떻게 사용하는지에 대해서는 이제 알겠다. 그렇다면 어떻게해서 슈퍼클래스의 메서드를 서브클래스가 사용하게되는 것인지 다음 그림을 보면서 이해해보자.
우리가 클래스를 정의하고 new 연산자를 통해 인스턴스를 생성하면 위와 같이 Heap 영역에는 인스턴스들이 옹기종기 모여있을 것이다. 여기서 Bottom 인스턴스 내부를 들여다보자.
그림에서 볼 수 있듯이 Bottom 인스턴스는 Top클래스를 상속받았기 때문에 new 연산자로 인스턴스가 생성될 때 슈퍼클래스의 인스턴스도 같이 생성된다. 또한, 슈퍼클래스는 서브클래스의 주소값을 동일하게 사용하는 것이다. 이러한 상태에서 슈퍼클래스가 보유한 메서드를 서브클래스가 재정의하면 최신화가 진행되는 것과 같다. 즉, 슈퍼클래스가 가진 메서드는 서브클래스가 가진 메서드에 비해 구식이라는 것이다. 이러한 이유로 서브클래스는 구식 메서드 보다는 자신이 방금 재정의해서 최신화시킨 메서드를 사용하게 되는 것이다. 이 특성은 슈퍼클래스로 형변환을 하여도 동일하게 작용한다. 슈퍼클래스의 입장에서도 서브클래스의 메서드가 더 최신의 것이기 때문에 서브클래스의 메서드를 사용하는 것이다. 이러한 원리로 예시에서 3번째 결과가 나오는 것이다.
이렇게해서 이번 게시글에서는 Java의 메서드 정의 기법 중 하나인 오버라이딩에 대해서 알아보았다. 오버라이딩을 통해 얻을 수 있는 효과는 다양하다. 예를 들어 클래스들의 틀이 되는 인터페이스를 선언하고, 공통되는 메서드를 선언만한다. 그리고 해당 인터페이스를 구현하는 클래스에서 메서드를 오버라이딩하여 사용하는 경우 공통된 객체를 구현하지만 클래스별로 유연성을 확보할 수 있으며 다형성을 충분히 활용하면서 메서드 사용이 가능하기 때문이다.
앞으로는 기능을 구현할 때 메서드 오버라이딩의 가능성을 염두해두고, 공통된 성격의 메서드가 있을 경우 슈퍼클래스 또는 인터페이스에 메서드를 선언하고, 상속받거나 구현하는 객체에서 오버라이딩을 하는 연습이 필요할 것 같다.
그럼 이만.👊🏽