일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스프링 빈
- Open EntityManager In View
- @Configuration
- open session in view
- 컴포넌트스캔
- 빈
- 싱글 스레드
- open-in-view
- Handler Adepter
- 불변 객체
- OSIV
- 생성자 주입
- 이펙티브 자바
- 익명 함수
- 가변 객체
- Spring Framework
- @FunctionalInterface
- mavenCentral
- Request flow
- Spring Batch
- Batch
- View Resolver
- spring boot
- Dispatcher Servlet
- 필드 주입
- 메서드 주입
- @componentScan
- 일괄처리
- @Bean
- 참조 타입
- Today
- Total
보다 더 나은 내일의 나를 위해
오버라이딩, 오버로딩 그 차이 본문
서론
주변 개발자 중에 오버라이딩과 오버로딩의 개념이 정확히 잡히지 않은 상태에서 이 둘을 혼동하는 경우를 봤습니다. 말이 비슷하기도 하고, 새로 다시 쓴다는 큰 틀에서의 개념도 비슷해서 그런 것 같습니다. 따라서 이번에는 오버라이딩과 오버로딩을 차례대로 알아보겠습니다.
또한 이 블로그에서는 Java를 기본 언어로 사용합니다.
오버라이딩
오버라이딩은 객체 지향에서 나온 개념입니다. 그러다 보니 위키피디아의 객체 지향 프로그래밍 문서를 보다 보면 다음과 같은 말을 볼 수 있습니다.
같은 이름의 메서드가 여러 클래스에서 다른 기능을 하는 것
실제로 오버라이딩은 클래스와 클래스 사이에서 사용됩니다. 정확히는 클래스간 상속 관계에서 자식 클래스에 사용됩니다.
오버라이딩은 매서드의 내용을 다시 쓰는 것입니다. 동작 방법을 바꿀 수 있는 거죠. 하지만 클라이언트에선 동작 방식을 알 필요가 없고 결과 값만을 원하기 때문에 반환 값이나 받는 인자가 달라지면 안됩니다.
말로 봤을 때는 되게 어려우니 차근차근 봅시다.
오버라이딩을 위한 조건
오버라이딩은 매서드의 선언부는 부모 클래스의 기존 메서드와 동일합니다. 하지만 이것만 지킨다면 내부 로직은 개발자 마음대로 작성해도 됩니다. 하지만 만약 타입 변환이 가능하다면 이 정도는 변경을 허용합니다.
말로 하면 어려우니 간단한 코드로 알아봅시다.
public class Parent {
public List<String> getList() {
return new ArrayList<>();
}
}
public class Child extends Parent {
@Override
public LinkedList<String> getList() {
return new LinkedList<>();
}
}
Parent라는 부모 클래스에 getList()라는 메서드가 있습니다. 이 메서드는 List를 반환한다고 선언되어있습니다.
또한 이 Parent를 상속받는 Child 클래스에서 getList()메서드를 오버라이딩하고있습니다. 하지만 LinkedList는 List로 cast 할 수 있으므로 반환 값을 LinkedList로 변경해서 오버라이딩하고 있습니다.
또한 접근 제어자를 부모 클래스의 메서드보다 다 작게 변경할 수 없습니다. 만약 부모 클래스에서 public으로 선언된 메서드를 자식 클래스에서 private로 범위를 줄일 수 없는 것이죠.
접근 제어자 뿐만 아니라 예외도 같습니다. 부모 클래스에서 RuntimeException을 던진다고 했을 때 그의 부모인 Exception을 예외로 던질 순 없지만, 그 반대로 부모 클래스에서 Exception을 던진다면 자식 클래스에서는 그보다 더 작은 범위인 RuntimeException을 던질 수 있죠.
오버라이딩은 언제 사용해
인터페이스를 사용한다면 여러분은 이미 오버라이딩을 아주 많이 사용하고있을 것입니다.
인터페이스의 구현체는 인터페이스에서 선언된 모든 메서드를 오버라이딩 하고있기 때문이죠. 이를 활용해 개발자는 한 인터페이스에 대한 여러 구현체를 만들어, 동작 방식은 다르지만 결과는 똑같이 나오도록 하는 코드를 작성할 수 있습니다.
아까 예제에서 사용했던 List 클래스를 살펴봅시다. List는 인터페이스입니다. 그리고 많은 구현체를 가지고 있죠.
대표적으로 ArrayList와 LinkedList가 있습니다. 실제로 둘의 동작 방식은 매우 다릅니다. 이 동작 방식 때문에 쓰임새에 따라 어떤 구현체를 선택할지 정해야 하기도 하죠. 하지만 성능이 굳이 중요하지 않다면 이것을 사용하는 개발자 입장에서는 구현체가 어떻게 동작하든 말든 상관이 없습니다. 둘 다 메서드를 사용하기 위해 넘겨줘야 하는 인자도 같고, 매서드 이름도 같으며, 반환해주는 결괏값도 같기 때문이죠.
또한 상속받은 클래스에서 부모 클래스의 동작과는 다르게 실행하고 싶을 때가 있을 것입니다. 예를 들면 부모 클래스에서는 "Parent"를 출력해야 하지만 자식 클래스에서는 "Child"를 출력해야 할 때와 같은 상황 말이죠. 이때 개발자는 오버라이딩을 통해 메서드의 내용을 수정할 수 있습니다.
아니면 자식 클래스에 클래스마다 다르게 동작해야 할 때도 사용할 수 있습니다. 만약 어느 게임에서 코드가 다음과 같은 상황이라고 가정을 해봅시다.
- 상위 클래스에 Player라는 클래스가 있다.
- Player 클래스를 상속받는 Attacker, Guard, Healer가 있다.
- 각각 skill() 매서드를 사용했을때 공격, 막기, 회복을 사용해야 한다.
이런 상황이라면 개발자는 Attacker, Guard, Healer 각각의 클래스에서 skill() 메서드를 오버라이딩 해 서로 다르게 동작하도록 만들 수 있습니다.
오버로딩
오버로딩 또한 위키피디아의 객체 지향 프로그래밍 문서에서 다음과 같은 말을 찾아 볼 수 있습니다.
같은 이름의 메소드가 인자의 개수나 자료형에 따라서 다른 기능을 하는 것
오버로딩은 한 클래스 내에서 사용됩니다. 또한 오버라이딩과는 달리 반환 값도 마음대로 변경할 수 있고, 받는 인자 또한 마음대로 변경할 수 있습니다.
정말 이름만 같은 함수들인 거죠.
오버로딩을 위한 조건
우선 메서드의 이름이 같아야 합니다.
메서드의 이름이 다르다면 그냥 그것은 다른 메서드일 뿐입니다.
또한 메서드 오버로딩을 하기 위해서는 매겨변수의 개수나 타입이 기존에 있는 메서드와 달라야 합니다.
만약 타입이 같다면 개수라도 달라야 하고, 개수가 같다면 타입이 하나라도 달라야 합니다.
public int getNum() {
return 1;
}
public int getNum(int n) {
return n;
}
public Integer getNum(int ...n) {
return Arrays.stream(n).sum();
}
public String getNum(String n) {
return n + "0";
}
여기 getNum() 메서드가 있습니다.
첫 번째 getNum() 메서드는 int를 반환하며 인자를 아무것도 받지 않습니다.
두 번째 getNum() 메서드는 첫 번째와 같이 int를 반환합니다. 하지만 인자를 하나 받음으로써 오버로딩을 할 수 있습니다.
만약 인자를 아무것도 받지 않는다면 그냥 같은 메서드가 2개이므로 컴파일 에러가 뜨게 됩니다.
세 번째 getNum() 메서드는 Integer를 반환합니다. 또한 인자를 동적으로 받습니다. 이렇게 하면 인자를 1개만 받든, 2개를 받든, 아예 받지 않든 상관없이 사용할 수 있습니다. 하지만 아예 받지 않거나 하나만 받게 한다면 기존 메서드와 반환 타입이 달라도 컴파일 에러가 뜨게 됩니다.
네 번째 getNum() 메서드는 String을 반환합니다. 그리고 인자를 하나만 받습니다. 하지만 기존 메서드에서 int로 하나만 받는 것과는 달리 String으로 받음으로써 오버로딩을 사용할 수 있게 하였습니다.
오버로딩은 언제 사용해
그렇다면 오버로딩은 언제 사용할까요.
바로 메서드의 동작 원리는 하나이지만 넘겨받는 인자에 따라 다르게 수행되어야 할 때 사용합니다.
ArrayList의 한 메서드를 살펴봅시다.
여기 세 add() 메서드가 있습니다.
첫 번째 메서드는 반환 값이 없으며 인자를 3개 받습니다. 또한 해당 클래스 내에서만 사용하므로 private로 선언되어 있습니다.
두 번째 메서드는 boolean을 반환하며 인자를 하나만 받습니다. 내부 동작은 우선 modCount를 1 추가하고 나머지는 첫 번째 메서드에 위임합니다. 또한 true를 반환해 주는군요.
세 번째 메서드는 반환 값이 없으며 인자를 2개만 받습니다. 리스트의 특정 인덱스에 값을 삽입하는 것이 목적인 add() 메서드입니다.
따라서 내부 동작은 첫 번째와 두 번째 메서드와는 꽤 다른 모습을 보여줍니다.
위 세 메서드 모두 주된 목적은 값을 리스트에 추가하는 것입니다. 하지만 받는 인자에 따라 메서드의 목적이 다르고, 동작이 다르고, 반환 값이 달라지죠. 이런 상황에서 개발자는 오버로딩을 활용할 수 있습니다.
차이점
오버라이딩과 오버로딩은 같은 메서드를 동작을 바꾼다는 면에서 보았을 때는 같습니다. 하지만 여러 가지 차이점이 있죠.
우선 오버라이딩은 클래스와 클래스 사이에서 사용하는 반면 오버로딩은 한 클래스 내에서 사용합니다.
오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 재정의할 때 쓰는 반면 오버로딩은 한 클래스 내에서 같은 목적의 매서드를 인자에 따라 다르게 동작하도록 하는 것이 목적이기 때문입니다.
또한 오버라이딩은 메서드 선언이 제한적이지만 오버로딩은 비교적 개방적입니다.
이 또한 둘의 목적에 따라 나온 차이점이죠.
'개발' 카테고리의 다른 글
클래스를 래핑 한다는 것 (0) | 2022.08.24 |
---|---|
dependency 찾기 (0) | 2022.07.30 |
제약있는 설계가 좋을까 나쁠까 (0) | 2022.07.04 |