객체지향을 공부하다 보면 나오는 개념 중 하나가 다형성이다. 그 다형성을 설명하기 위해 나오는 키워드가 Overriding과 Overloading이다.
물론 다형성은 상위 클래스와 하위 클래스 사이에서도 이야기할 수 있고, 인터페이스와 구현 클래스 사이에서도 이야기할 수 있지만 가장 기본은 오버라이딩과 오버로딩이다.
관점에 따라 오버로딩은 다형성인지에 대해서는 이견이 존재한다.
하지만 객체지향엔 정답이 없으니 상관없다.
해당 키워드들을 비교하는 것은 자바 객체지향에 있어 클래식한 질문 중 하나이다. 왜냐하면 두 용어는 이름은 비슷하지만 전혀 다른 의미로 사용되기 때문이다.
오버라이딩과 오버로딩?
- 같은 메서드 이름, 같은 인자 목록으로 상위 클래스의 메서드를 재정의
- 같은 메서드 이름, 다른 인자 목록으로 다수의 메서드를 중복정의
두 키워드를 정의하고 있는 문장들이다. 첫 문장은 오버라이딩을 나타내는 말이고, 두번째 문장은 오버로딩을 나타낸다.
어떤 차이점을 나타내고 있는 것인지 알아보자.
오버라이딩
상속 관계에 있는 상위 클래스의 정의한 메서드가 하위 클래스에서 구현할 내용과 맞지 않을 경우 하위 클래스에 이 메서드를 재정의 하는것을 의미하며 오버라이딩을 하려면 반환형, 메서드 이름, 매개변수 개수, 매개변수 자료형이 같아야 한다.
쉽게 말해, 메서드의 이름이 서로 같고, 매개변수가 같고, 반환형이 같을 경우에 상속받은 메서드를 덮어쓴다고 생각하면 된다. 이 경우 '부모클래스의 메서드는 무시하고, 자식 클래스의 메서드 기능을 사용하겠다'의 의미로 사용된다.
상위 클래스 Customer 클래스가 있다고 가정하자.
해당 클래스에는 제품가격을 계산하는 calcPrice() 메서드가 정의되어 있고 정가 그대로 계산이 된다. 만약 VIP 고객은 10% 할인된 가격에 계산이 된다고 가정하였을 때 오버라이딩 관점에서 고려해 보자.
// Customer 클래스에 구현된 calcPrica() 메서드
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;
return price;
}
일단 VIPCustomer 클래스를 생성하고 상위 클래스인 Customer 클래스를 상속받아야 한다. 그런 다음 calcPrice() 메서드의 구현부를 재정의하여 사용한다.
public class VIPCustomer extends Customer {
private int agentID;
double saleRatio;
[...]
// 오버라이딩 재정의
@Override
public int calcPrice(int price){
bonusPoint += price + bonusRatio; // 보너스 포인트 계산
return price - (int)(price * saleRatio); // 할인된 가격을 계산하여 반환
}
}
조건에 맞게 정의되면 오버라이딩의 되며 @Override 어노테이션을 선언하여 오버라이딩임을 명시적으로 나타낼 수 있다. (조건만 맞다면 생략가능)
형 변환과 주의할점
Customer customer = new VIPCustomer("10030", "옷", 2000);
customer.calcPrice(10000);
라고 호출하면 어떻게 동작할까?
자바는 항상 인스턴스의 메서드가 호출이 된다. (가상 메서드의 원리) 그러므로 customer 변수의 타입은 Customer 클래스지만 인스턴스 타입인 VIPCustomer의 메서드가 호출된다.
오버라이딩을 선언할 때 주의할 점은 아래 3가지와 같다.
1. 오버라이딩의 접근제이자는 부모 클래스의 메서드의 접근 제어자보다 좁은 범위의 접근 제어자를 사용할 수 없다.
부모클래스의 접근제어자는 default로 설정되어 있다. 여기서 자식 클래스들은 default보다 같거나 더 넓은 범위의 접근제어자만 설정할 수 있으므로 default, protected, public 이 세 개의 접근 제어자는 사용이 가능하다.
2. 예외(Exception)는 부모 클래스의 메서드 보다 많이 선언될 수 없다.
부모 클래스에서 어떤 예외를 throws 한다고 했을 때, 자식 클래스에서는 그 예외보다 더 큰 범위의 예외를 throws 할 수 없다.
3. static메서드를 인스턴스의 메소드로 또는 그 반대로 바꿀 수 없다.
부모 클래스의 static메소드를 자식에서 같은 이름으로 정의할 수 있지만 이것은 다시 정의하는 것이 아니라 같은 이름의 static메소드를 새로 정의하는 것이다.
오버로딩
한 클래스 내에서 이름 사용되고 있는 메서드를 중복선언하여 사용하기 위해 사용한다. 같은 이름으로 선언하고 매개변수의 개수 또는 타입을 다르게 지정하면 동일 이름의 메서드가 정의 가능하다.
즉, 메서드의 이름은 같고, 매개변수의 개수나 타입이 달라야 한다. 여기서 주의할 점은 리턴값만 다를 경우 오버로딩의 조건에 포함되지 않는다.
class OverloadingTest{
void print(){
System.out.println("오버로딩1");
}
void print(int a, int b){
System.out.println("오버로딩2");
}
void print(String c){
System.out.println("오버로딩3");
}
}
public class Main {
public static void main(String[] args) {
OverloadingTest ot = new OverloadingTest();
//매개변수가 없는 print() 호출
ot.print();
//매개변수가 int형 두개인 cat() 호출
ot.print(20, 80);
//매개변수가 String 한개인 cat() 호출
ot.print("오버로딩 예제입니다.");
}
}
// 결과
오버로딩1
오버로딩2
오버로딩3
위 코드들은 동일 이름의 메서드지만 문제없이 사용이 된다. 매개변수를 각각 다르게 지정했기 때문이다. 오버로딩은 접근제어자나 리턴값도 자유롭게 변경하여 사용가능하지만 매개변수의 차이를 두지 않고 단독으로는 오버로딩의 조건으로 사용될 수 없다.
결국 오버로딩은 매개변수의 차이로만 구현이 된다.
Reference
자바의 정석, Do it! 자바 프로그래밍 입문, 스프링 입문을 위한 자바 객체 지향의 원리와 이해 책을 참고하여 작성하였습니다.
'Language > Java' 카테고리의 다른 글
[JAVA] 람다식과 함수형 인터페이스 (0) | 2024.05.02 |
---|---|
[인프런 워밍업 스터디 클럽 1기] 어노테이션의 역할 (0) | 2024.04.30 |
[Java] String[]을 List , List를 String[]로 변환하는 방법 (0) | 2023.10.24 |
[스진초5기/Java] 오버라이딩을 구현할 때 꼭 @Override가 필요할까? [+어노테이션의 역할] (0) | 2023.10.23 |
[Java] 인터프리터와 컴파일러 그리고 Java (1) | 2023.10.23 |