method overriding(메소드 재정의)은 부모 클래스에게 method를 상속받은 자식 클래스 또는 인터페이스를 구현한 구현 클래스에서, 해당 클래스에 적합하지 않거나 별개의 작업이 필요한 부모 클래스의 메소드 실행내용을 변경해 적용하는 작업이다. 이를 통해 같은 클래스를 상속받거나 구현한 클래스 간에도 다형성을 구현할 수 있다. 또한 추상 메소드(abstract method)는 실행부가 존재하지 않으므로, 반드시 재정의가 필요하다.
어제 포스팅한 객체의 네 가지 특성 중 상속성 항목의 마지막에 @Override annotation에 대해 간략하게 언급했었다. 클래스 상속 후 메소드를 재정의할 때, 자식 클래스에서는 해당 메소드의 선언부를 부모 클래스의 메소드 선언부와 동일하게 작성해야 한다. 또한 새로운 예외를 발생시킬 수 없으며, 접근 제한자의 경우 부모 클래스에서보다 공개적인 제한자로의 변경은 허용된다.
class Parent {
public void hello(String name) {
System.out.println("안녕하세요, 저는 " + name + "입니다.");
}
}
class Child extends Parent{
public void hello(String name) {
System.out.println("안녕!");
}
}
위의 예시에서 Child 클래스는 Parent 클래스를 상속하고, hello 메소드를 재정의하고 있다.
부모 클래스 메소드의 접근 제한자는 public이므로, 더 낮은 수준의 접근 제한을 할 수 없다. 따라서 자식 클래스에서도 반드시 public으로 선언해야 한다.
return 타입과 메소드명, 매개변수의 타입 또한 부모 클래스에서와 같이 각각 void, hello, String로 선언하도록 제한되어 있다. 매개변수명은 실질적인 영향이 없으므로 반드시 같을 필요는 없지만, 가독성과 상호관계 파악을 위해 동일하게 작명하는 편이 좋다.
@Override annotation을 생략한 경우
public class Annotation {
public static void main(String[] args) {
Parent parent = new Child();
String mainMethod = "Main Method";
parent.hello(mainMethod);
}
}
class Parent {
public void hello(String name) {
System.out.println("안녕하세요, 저는 " + name + "입니다.");
}
}
class Child extends Parent{
public void hello() {
System.out.println("안녕!");
}
}
그러나 위와 같이 자식 클래스의 메소드에서 매개변수가 생략되더라도 프로그램은 정상 실행될 수 있다. 코더의 원래 의도와는 다르게 재정의가 이루어지지 않았지만 compiler는 이를 알 수 없으므로, 별개의 메소드를 선언한 것으로 받아들인다.
상부의 메인 메소드에서 자식 클래스의 객체를 생성하고 자동 타입 변환(promotion)을 통해 부모 타입 변수에 대입했지만, 메소드를 호출하면 재정의되지 않은 부모 클래스의 메소드가 호출된다. 코더가 재정의 여부를 알지 못한 채로 넘어간다면 이후 디버깅 과정에서 많은 시간을 낭비하게 될 수도 있다.
이와 같은 상황을 방지하려면 @Override annotation을 통해 compiler에게 자신이 재정의를 할 것이라는 사실을 알린 후, 재정의가 이루어지지 않은 경우 오류를 출력하게 할 수 있다.
@Override annotation을 작성한 경우
class Parent {
public void hello(String name) {
System.out.println("안녕하세요, 저는 " + name + "입니다.");
}
}
class Child extends Parent{
@Override
public void hello() {
System.out.println("안녕!");
}
위와 같이 @Override를 작성한 경우, 오류가 출력되며 compile되지 않는다. 따라서 코드를 의도에 맞게 재정의할 수 있도록 조기에 수정할 수 있다.
IntelliJ에서는 부모 클래스에 재정의할 수 있는 기존의 method가 존재하지 않으므로, 새로운 추상 메소드를 선언할 것을 권하고 있다.
자식 클래스의 메소드에서 String 매개변수를 선언해 재정의가 제대로 이루어진 경우, 정상적으로 재정의된 작업이 실행된다.
물론 재정의가 제대로 되었을 경우 @Override annotaion은 생략이 가능하지만, 불상사를 대비하기 위해서라도 반드시 작성해 주는 편이 좋을 것이다. 또한 최초에는 제대로 재정의가 이루어졌더라도 부모 클래스의 업데이트로 인해 메소드명이 변경되었을 경우, @Override annotation이 없으면 오류가 발생하지 않아 재정의가 해제된 사실을 알아차리지 못하고 넘어갈 가능성이 생긴다. 또 @Override annotation은 재정의 여부를 한 눈에 쉽게 알아볼 수 있게 해 주어, 코드의 가독성을 향상시킬 수 있다.
'Development > Java' 카테고리의 다른 글
배열 출력, 복제, List 또는 Set에서 변경 (0) | 2023.04.22 |
---|---|
collection framework의 interface(List, Set, Map)별 주요 method 정리 (0) | 2023.04.02 |
인터페이스의 객체지향적 특징 (0) | 2023.03.28 |
객체지향의 네 가지 특성 : 캡슐화, 추상화, 다형성, 상속성 (0) | 2023.03.28 |
(객체지향 기초) Java로 계좌 관리 프로그램 만들기(2) (0) | 2023.03.18 |