다형성(Polymorphism) - "이름은 하나인데~ 별명은 여러 개~"
정의
내 동생 곱슬머리 개구쟁이 내 동생이라는 동요를 알고 있는가?
내동생 곱슬머리
개구장이 내동생 이름은 하나인데
별명은 서너개
엄마가 부를때는 꿀돼지
아빠가 부를때는 두꺼비
누나가 부를때는 왕자님
어떤게 진짜인지
몰라몰라 몰라
내동생 곱슬머리
개구장이 내동생 이름은 하나인데
별명은 서너개
잘먹고 건강하게 꿀돼지
착하고 복스럽게 두꺼비
용감하고 슬기롭게 왕자님
어떤게 진짜인지
몰라몰라몰라
가사를 보면, 동생이라는 존재는 하나인데 그를 부르는 이름, 즉 그가 정의되는 방식은 여러 개임을 볼 수 있다. 다형성은 이와 같이 이름은 하나인데~ 별명은 서너 개~로, 하나의 객체를 여러 개의 타입으로 선언할 수 있는 개념을 말한다.
자, 다시 다형성을 정의해보자. 다형성은 하나의 무언가가 다양한 형태로 정의될 수 있음을 의미한다.
(여기서 "무언가"는 객체 뿐만 아니라, 메서드까지도 가능하다.)
왜? & 어떻게?
어느 때나처럼 목적은 "개발자가 편하려고"이다. 역시 중복을 제거하고 소스 변경을 줄일 수 있다.
람보르기니가 빠른 것을 깨달으려면 자전거부터 타라는 말이 있지 않은가. 자, 우리 한 번 느려터져보자.
오버로딩
같은 이름의 메서드를 매개변수의 유형과 개수를 다르게 하여 그에 맞게 호출되는 개념이다.
오버로딩은 메서드나 생성자에서 사용할 수 있다.
아래 소스는 오버로딩을 사용하지 않은 예시이다.
예시 소스는 자바 기본 소스인 PrintStream에서 오버로딩을 뺀 것이다.
public void printlnWithInt(int x) {
synchronized (this) {
print(x);
newLine();
}
}
public void printlnWithLong(long x) {
synchronized (this) {
print(x);
newLine();
}
}
public void printlnWithFloat(float x) {
synchronized (this) {
print(x);
newLine();
}
}
public void printlnWithDouble(double x) {
synchronized (this) {
print(x);
newLine();
}
}
public void printlnWithCharArray(char x[]) {
synchronized (this) {
print(x);
newLine();
}
}
public void printlnWithString(String x) {
synchronized (this) {
print(x);
newLine();
}
}
이러면 뭐가 불편할지 예상이 되는가?
1. 일단 동작이 똑같은데 다른 이름을 갖고 있다. 이는 로직에 변경이 생긴다면, 하나하나 고쳐줘야 한다는 말이다..!
2. 저 메서드를 사용할 때를 생각해보자. 함수 이름에 println이라는 동작 뿐만 아니라, 매개변수가 어떤 타입인가?라는 정보까지 들어가 있다. 이는 사용자는 이 두 가지를 고려해서 호출해야하고, 매개변수가 바뀌면 이에 따라 함수명도 바꿔주어야 함을 의미한다.
자, 이제 조금 빨라져보자.
아래 소스는 본래 PrintStream 소스 코드이다.
솔직히 별로 달라진 것 없어 보이지만, 이제 사용할 때는 완전히 편할 것이 보이지 않는가~~?
오버라이딩
상속에서 나온 오버라이딩은 상위 클래스에서 상속받은 메서드를 하위 클래스에서 재정의하는 것을 의미한다.
오버라이딩을 사용하지 않았을 때의 불편함은 상속 글에서 확인할 수 있다.
인터페이스
implements, extends 등 키워드를 들어본 적 있을 것이다.
아래 소스는 앞서 말한 동요를 코드로 표현한 것이다.
public class Polymorphism_MyBrother {
public static void main(String[] args) {
HoneyPig bro = new MyBrother();
Toad broSame = new MyBrother();
Prince broSameHere = new MyBrother();
}
}
interface HoneyPig{}
interface Toad{}
interface Prince{}
class MyBrother implements HoneyPig, Toad, Prince{
void sayHello (){
System.out.println("안녕하세요");
}
}
main 메서드를 보면 MyBrother가 다앙한 타입으로 정의됨을 볼 수 있다. 이는 자신의 상위 인터페이스로 정의될 수 있는 "업 캐스팅" 개념이 있기에 가능하다.
이와 같이 상속과 구현을 통해 다양하게 정의할 수 있다.
오늘은 다형성의 무엇인가? 왜 쓰는가? 어떻게 쓰는가?를 알아보았다.
오늘 내용이 이해가 잘 가지 않았다면 지금까지의 글을 다시 보기하면 해결될 것이다.
더 깊게 알아보고 싶다면 "OCP", "DIP"을 검색해보면 될 것 같다.
오늘을 마지막으로 객체지향프로그래밍 특성 정리를 마무리하고자 한다.
용두사미, 어영부영하게 끝나서 아쉽다. 다음 시리즈가 있다면 재깍재깍 야무지게 올려보도록 하겠다..!
'방법 > 객체지향프로그래밍' 카테고리의 다른 글
객체지향프로그래밍 (OOP) - 구성 (0) | 2022.12.01 |
---|---|
객체지향프로그래밍 (OOP) - 상속 (0) | 2022.10.22 |
객체지향프로그래밍 (OOP) - 캡슐화 (2) | 2022.09.22 |
객체지향프로그래밍 (OOP) - 추상화 (0) | 2022.09.07 |
객체지향프로그래밍 (OOP) - 서문 (0) | 2022.08.22 |