이름은 같고 투입구만 다르다
지금까지 아무 생각 없이 써 온 것이 있습니다.
Console.WriteLine(42);
Console.WriteLine("안녕");
Console.WriteLine(3.14);
Console.WriteLine(true);
넣는 값의 타입이 전부 다른데 이름은 하나입니다. 메서드는 매개변수 타입을 정해서 만든다고 했는데, 어떻게 다 받을까요.
답은 간단합니다. WriteLine 이 한 개가 아닙니다.
숫자를 받는 것, 문자열을 받는 것, 실수를 받는 것이 따로 있고 이름만 같습니다.
하는 일이 같으면 이름도 같아야 읽힙니다. 받는 것만 다르게 여러 벌 만듭니다.
이걸 오버로딩이라고 합니다.
이름이 하나여야 읽힌다
오버로딩이 없으면 이렇게 됐을 겁니다.
Console.WriteLineInt(42);
Console.WriteLineString("안녕");
Console.WriteLineDouble(3.14);
쓰는 쪽이 매번 “지금 넣는 게 무슨 타입이더라” 를 생각해야 합니다. 타입을 바꾸면 메서드 이름도 같이 고쳐야 합니다.
하는 일이 같은데 이름이 다르면 부담은 쓰는 쪽이 집니다. 오버로딩은 그 부담을 만드는 쪽으로 옮깁니다.
void Print(int value)
{
Console.WriteLine("숫자: " + value);
}
void Print(string value)
{
Console.WriteLine("글자: " + value);
}
Print(42); // 숫자: 42
Print("안녕"); // 글자: 안녕
컴파일러가 넣은 값의 타입을 보고 어느 쪽을 부를지 고릅니다. 부를 때 정해지는 것이 아니라 컴파일할 때 정해집니다.
무엇을 보고 고르나
컴파일러가 보는 것은 매개변수의 타입, 개수, 순서 셋뿐입니다.
void Print(int a) // 타입이 다르다
void Print(string a)
void Print(int a) // 개수가 다르다
void Print(int a, int b)
void Print(int a, string b) // 순서가 다르다
void Print(string a, int b)
전부 됩니다. 셋 중 하나라도 다르면 다른 메서드로 봅니다.
반환 타입은 보지 않습니다.
int GetValue()
string GetValue() // 컴파일 에러
왜 안 되는지는 이렇게 생각하면 됩니다.
GetValue();
이 줄만 보고 어느 쪽을 불러야 할지 정할 수가 없습니다. 돌려받은 값을 안 쓰는 코드도 되니까요. 고르는 근거는 넘기는 것에만 있어야 합니다. 매개변수가 완전히 같으면 이름을 다르게 지어야 합니다.
매개변수 이름도 안 봅니다.
void Print(int score)
void Print(int point) // 컴파일 에러
앞에서 매개변수 이름은 메서드 안에서만 통한다고 했습니다. 밖에서는 안 보이는 이름이니 고르는 근거가 될 수 없습니다.
기본값을 정해 두면 생략할 수 있다
거의 같은데 매개변수 하나만 있고 없고인 경우가 있습니다.
void Greet(string name)
{
Greet(name, "안녕하세요");
}
void Greet(string name, string message)
{
Console.WriteLine(name + "님, " + message);
}
두 벌을 만들 것 없이 기본값을 적어 두면 됩니다.
void Greet(string name, string message = "안녕하세요")
{
Console.WriteLine(name + "님, " + message);
}
Greet("서병필"); // 서병필님, 안녕하세요
Greet("서병필", "오랜만입니다"); // 서병필님, 오랜만입니다
규칙이 하나 있습니다. 기본값이 있는 매개변수는 목록 맨 뒤에 와야 합니다.
void Greet(string message = "안녕", string name) // 컴파일 에러
앞에 있으면 Greet("서병필") 이 어느 자리에 넣은 것인지 알 수 없습니다.
넣은 값은 앞에서부터 차례로 채워지기 때문입니다.
이름을 붙이면 순서를 안 지켜도 된다
기본값이 여러 개일 때 문제가 생깁니다.
void Setup(int width = 800, int height = 600, bool fullscreen = false)
fullscreen 만 바꾸고 싶은데, 앞에서부터 채워지니 앞의 두 개를 다 적어야 합니다.
Setup(800, 600, true); // 안 바꿀 값을 굳이 적었습니다
이럴 때 이름을 붙여서 넘깁니다.
Setup(fullscreen: true);
컴파일러가 이름을 보고 어느 자리인지 압니다. 순서를 지킬 필요도 없습니다.
Setup(height: 1080, width: 1920);
이름을 붙이면 읽기도 좋아집니다.
CreateWindow(1920, 1080, true, false);
CreateWindow(width: 1920, height: 1080, fullscreen: true, resizable: false);
아래쪽은 주석 없이도 읽힙니다.
true 나 false 를 여러 개 넘길 때는 이름을 붙이는 편이 낫습니다.
숫자만 봐서는 무엇을 켜고 끄는지 알 수 없습니다.
어디까지 묶을 것인가
오버로딩은 하는 일이 같을 때 씁니다. 이름이 같으니 쓰는 사람은 같은 일을 기대합니다.
double Average(int a, int b)
double Average(List<int> nums)
이건 좋습니다. 둘 다 평균을 구하고, 재료만 다릅니다.
void Save(string path) // 파일에 저장
void Save(int userId) // 서버에 전송
이건 이름을 나눠야 합니다. 하는 일이 다릅니다. 같은 이름이 붙어 있으면 읽는 사람이 같은 일이라고 믿습니다. 오버로딩은 문법이 허락하는 범위가 아니라 읽는 사람의 기대에 맞춰 씁니다.
정리
- 이름이 같고 매개변수가 다른 메서드를 여러 벌 만드는 것이 오버로딩입니다.
- 컴파일러가 넘긴 값의 타입, 개수, 순서를 보고 고릅니다. 컴파일할 때 정해집니다.
- 반환 타입과 매개변수 이름은 고르는 근거가 아닙니다. 그것만 다르면 컴파일 에러입니다.
- 매개변수 하나만 있고 없고라면 오버로딩 대신 기본값을 씁니다.
- 기본값이 있는 매개변수는 목록 맨 뒤에 와야 합니다.
- 이름을 붙여 넘기면 순서를 안 지켜도 되고, 읽기도 좋아집니다.
- 하는 일이 다르면 이름을 나눕니다. 같은 이름은 같은 일이라는 약속입니다.