지난 포스팅에서는 클래스 사이 상속 및 포함 관계에 대해 알아보았다. 상속관계에 있는 클래스에서, 하위(자식) 클래스는 아무런 정의가 되어 있지 않더라도 상위(부모)로 지정된 클래스의 멤버변수와 매서드를 마음대로 가져와서 쓸 수 있음을 지난 포스팅에서 확인했었다.
그런데, 상위 클래스의 매서드를 빌려 쓰는 것도 문제가 될 수 있다. 예를 들어, 사람이라는 객체를 클래스로 정의했다고 해보자. 클래스에는 이 사람의 성별, 나이, 직업, 연봉 등의 정보가 포함되어 있다고 하자. 그리고 사람의 정보를 출력하는 매서드가 아래와 같이 정의되어 있다고 가정하자.
이 Human 클래스 하위에 Adult와 Child 클래스를 생성한다고 해보자. 그럼, Adult와 child 모두 print_info() 매서드를 사용할 수 있게 된다. 하지만 Child 클래스는 성인이 아닌 사람을 정의한 클래스인데, 이들은 직접 벌어들이는 수익이 없기 때문에 print_info() 내용을 바로 사용하기에 문제가 있다. 따라서 상위 클래스의 print_info()를 사용하기보다 다른 매서드를 만들어 조금 더 미성년자에 맞는 정보를 출력해 줄 수 있는 매서드를 만들어야 할 필요가 있다.
그럼, 하위 클래스마다 매서드의 행동 양식이 조금씩 다르다면, 매 번 새로운 이름으로 매서드를 생성해주어야 하는 걸까? 지금까지 Java에 대해 어느정도 학습하신 분들이라면 대략적으로 이러한 불편함도 Java에서는 해결할 수 있는 솔루션이 존재할 것이라 생각할 것이다. 맞다. 상속 관계의 클래스에서 매서드 이름 변경없이 결과를 다르게 만들 수 있는데 이를 오버라이딩(Overriding)이라고 한다.
오버라이딩을 조선말로 바꾸기가 참 어려운데, 깔끔하게 대응하는 단어가 없기 때문이다. 따라서 필자는 보기만해도 울렁거리는 영문 사전에서 Override에 대한 정의를 확인해보았다.
해석해보면 오버라이드는 "권한에 대한 거부나 취소" 또는 "어떠한 자동적인 행동을 방해하고 수동으로 전환하는 것"을 의미한다. Java에서도 이 의미를 크게 벗어나지 않는데, 하위 클래스가 상위 클래스에 정의된 매서드를 따르지 않고 자신의 매서드를 실행하는 것을 오버라이드라고 하기 때문이다(마치 부모 말 안듣는 후* 자식 새*의 느낌이다).
이 오버라이드의 의미를 가장 잘 확인할 수 있는 영화가 있다. 필자가 한 때 가장 좋아했던 인터스텔라.
만 박사가 주인공 일행 배반을 때리고 엔듀런스(Endurance)호에 도킹을 시도하려하지만, 권한이 없어 도킹이 되지 않을 때 오버라이드(Override)라는 말을 한다.
Dr.Mann : "Override"
Voice System: "Unauthorized"
정리하자면, 오버라이드(Override)는 "정의된 권한, 행위에 대해 내가 직접 수정을 가하여 실행하겠다"라는(쿠테타??) 의미를 내포하고 있다고 보면 된다.
서론이 많이 길었다. 본격적으로 상속 관계 클래스의 오버라이드에 대해 알아보자.
1. 오버라이드(Override)
위의 예시에서 Human과 Adult, Child 클래스는 print_info()라는 매서드를 공유한다. 그리고 필자의 의도대로 Child 클래스는 print_info()의 결과가 조금 다르게 출력되도록 만들려고 한다.
오버라이드는 매우 쉽다. 하위 클래스에도 상위 클래스와 동일한 print_info() 매서드를 만들어보자. 대신, 상위 및 하위 클래스의 각 매서드 내에, 각 클래스의 이름이 출력되도록 코드를 추가해보자.
이제, Adult와 Child 클래스로부터 인스턴스를 하나씩 생성하여 print_info() 매서드를 실행하고 그 결과를 확인해보자.
두 인스턴스가 참조하는 print_info()가 다른 것을 확인할 수 있다. Human 클래스와 동일한 내용의 매서드가 정의되어 있지 않은 Adult의 인스턴스는 Human 클래스의 print_info() 매서드를 호출하는 반면, Child의 인스턴스는 Child 클래스 내에 정의된 print_info() 매서드를 호출하는 것을 확인할 수 있다.
이제, Child 클래스 내의 매서드 내용을 원래 필자가 표현하려고 했던 내용으로 변경해보자.
즉, 오버라이드는 상위 클래스에 정의된 매서드의 내용을 변경하는 것이 전부다. 크게 어려운 내용은 아니다.
Java를 처음 접하는 분들은 이전의 포스팅에서 보았던 매서드 오버로딩(Overloading)과 비슷한 용어로 인해 헷갈릴 수 있다. 오버로딩은 하나의 클래스 내부에서 동일한 이름의 매서드를 입력값에 따라 서로 다른 결과를 반환하는 반면 오버라이딩은 상속 관계의 클래스에서 동일한 이름의 매서드에 대해 서로 다른 결과를 나타내는 반환하는 것이다. 다른 결과를 낸다는 것은 동일하나, 클래스 내부의 매서드끼리인지, 아니면 서로 상속관계로 정의된 클래스의 매서드끼리인지에 따라 용어가 달라진다.
오버라이딩 사용 시 주의할 점은 아래와 같다.
- 상위 클래스에 정의된 매서드의 이름, 입력 매개변수와 동일하게 하위 클래스에 정의되어야 한다.
- 상위 클래스에 정의된 매서드의 출력값 타입과 하위 클래스에 정의된 오버라이드 매서드의 출력값 타입이 동일해야 한다.
class 상위클래스명
{
매서드반환타입 매서드명 (매서드입력인자) { 매서드 수행 코드1 };
}
...
class 하위클래스명
{
매서드반환타입 매서드명 (매서드입력인자) { 매서드 수행 코드2 };
}
2. 상위클래스 멤버변수 및 매서드 호출을 위한 super 예약어
여기서 궁금한 점이 하나 생긴다. 도대체 하위 클래스는 어떻게 상위 클래스의 멤버 변수와 매서드를 호출할 수 있는 것일까?
Java는 프로그래머가 작성하지 않은 코드 중 반드시 필요한 코드는 JVM에서 자동으로 추가하도록 되어 있다. 이전에 보았던 생성자에 대한 포스팅에서도 확인한 내용이지만, 클래스의 기본 생성자를 정의하지 않아도 JVM이 알아서 클래스의 기본 생성자 코드를 추가해주는 것처럼 말이다. 상위 클래스를 상속하는 하위 클래스의 경우, 클래스 명 뒤에 extends 예약어가 붙으면 JVM에서 클래스 생성자코드와 상위 생성자 코드를 자동으로 작성해준다.
생략된 코드의 내용을 보면, 하위 클래스 생성자 내에는 상위 클래스명으로 생성자 코드가 작성되지 않고 super라는 이름으로 생성자 코드가 작성되어 있다. 즉, Java에서 super라는 코드는 상위 클래스를 나타내는 예약어라 보면 된다.
super 예약어는 멤버 변수 호출 시 주의해야 할 점이 하나 있다. 만약 하위 클래스에서 특정 멤버 변수가 특정 리터럴 값으로 초기화된 경우, 상위 클래스의 멤버 변수 값이 호출되는 것이 아니라 하위 클래스의 값이 호출된다.
3. 최상위 클래스 - Object
이제, 위의 예시에서 만든 세 클래스에서 toString()이라는 매서드를 만들어보자. 반환값 타입은 String으로 지정하고, 반환하는 리터럴 값은 각 클래스의 이름 + "클래스 print"로 지정하자. 단, 반환값 타입 앞에 public이라는 단어를 추가해주자.
** public 예약어는 추후 포스팅 할 제어자와 관련된 내용이다. 지금은 단순히 toString() 매서드를 클래스에서 사용하기 위해 public을 붙여주어야 한다는 것만 알고 있자.
이제 main 함수에서 각 클래스에 대한 인스턴스를 생성하고, 해당 인스턴스의 변수명을 System.out.print 매서드로 출력해보자. 인스턴스 변수는 참조형 변수이기 때문에 메모리 주소가 화면에 출력될 것이라 예상하지만, 의외의 toString() 매서드의 반환값이 화면이 출력된다.
도대체 어떻게 이런 결과가 나타나는 걸까? toString에 대해서는 단지 값을 반환했을 뿐인데 말이다.
사실 Java는 클래스 생성 시 자동으로 최상위 클래스를 상속하게 된다. 이 최상위 클래스는 Java.lang 패키지의 Object 클래스다. 즉, Java에서 프로그래밍으로 작성하는 모든 클래스는 extends Object 라는 구문이 클래스명 뒤에 생략되어 있다고 보면 된다.
그리고 이 최상위 클래스에는 toString()이라는 이름의 매서드가 존재한다.
이 매서드는 Object 클래스를 대표하는 "문자열"을 반환한다. 클래스 내에 이 toString() 매서드가 오버라이딩 된 경우, 클래스 인스턴스를 print 할 경우 toString 반환값을 출력하게 된다.
다음 포스팅에서는 패키지와 클래스, 그리고 import에 대해 알아보려 한다.
Fin.
'Java > Java Basic' 카테고리의 다른 글
[Java Basic] 19. 접근 제어자(Access Modifier) (0) | 2022.07.21 |
---|---|
[Java Basic] 18. 패키지와 Import (0) | 2022.07.16 |
[Java Basic] 16. 클래스의 상속관계와 포함관계 (0) | 2022.07.11 |
[Java Basic] 15. 클래스 생성자와 초기화블럭 (0) | 2022.07.10 |
[Java Basic] 14. Java 매서드 오버로딩(Overloading)과 가변인자 (0) | 2022.07.06 |
댓글