-
Notifications
You must be signed in to change notification settings - Fork 0
Description
section: 8장 메서드
- 아이템 52: 다중정의는 신중하게 사용하라
- 아이템 53: 가변인수는 신중하게 사용하라
🍵 서론
다중 정의와 가변 인수 메서드를 알고는 있지만 내가 자주 만들어 사용하지 않았다!..
이번 기회에 확실하게 알아보도록 합시다!
🌒 본론
아이템 52
1. 다중정의 ( overloading )
다중정의란 동일한 이름에 대하여 여러 가지 의미를 부여하는 것이다. 1)
다중정의 메서드 사이에서는 객체의 런타임 타입은 전혀 중요하지 않고, 오직 컴파일 타임에 매개변수의 컴파일 타입에 의해 선택이 이뤄진다.
아래의 예시 코드를 한번 살펴보자
public class Runner {
private static NumberClassifier classifier = new NumberClassifier();
public static void main(String[] args) {
Number[] array = {1, 1.0, Long.MAX_VALUE};
for (Number number : array) {
System.out.println(classifier.classify(number));
}
}
}
public class NumberClassifier {
public String classify(Integer number) {
return "Integer";
}
public String classify(Double number) {
return "Double";
}
public String classify(Number number) {
return "Number";
}
}
/* Result
Number
Number
Number
*/위에서 적힌 것 처럼 컴파일 타입이 Number이기 때문에 숫자 분류기에서 Number만 출력합니다. 위의 코드를 올바르게 사용하려면 다음과 같이 모든 메서드를 하나로 합친 후 instanceof로 명시적으로 검사하면 해결됩니다.
public String classify(Number number) {
return number instanceof Integer ? "Integer" :
number instanceof Double ? "Double" :
"Number";
}
/* Result
Integer
Double
Number
*/2. 다중정의 올바르게 사용하기
- 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 않습니다.
- 가변인수를 사용하는 메서드라면 다중정의를 아예 하지 말아야 합니다. [[Item53. 가변인수는 신중히 사용하라]]
- 상대적으로 더 특수한 다중정의 메서드에서 덜 특수한(더 일반적인) 다중정의 메서드로 일을 넘겨버립니다.
// 인수를 포워드하여 두 메서드가 동일한 일을 하도록 보장
pulbic boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence) sb);
}다중정의하는 대신 메서드 이름을 다르게 지어주는 방법이 있습니다.
3. 다중정의 주의사항
3.1. 생성자 다중정의
생성자는 이름을 다르게 지을 수 없으니 두 번째 생성자부터는 무조건 다중정의가 됩니다.
하지만 정적 팩토리라는 대안을 활용할 수 있는 경우가 많습니다. #4
3.2. 오토박싱과 다중정의
매개변수 수가 같은 다중정의 메서드가 많더라도, 매개변수중 하나이상이"근본적으로 다르다"면 헷갈릴 일이 없습니다. 다중정의 메서드를 호출하는 것은 매개변수들의 런타임 타입만으로 결정이됩니다.
💫 Info
근본적으로 다르다는 건 두 타입의 값을 서로 어느 쪽으로든 형변환할 수 없다는 뜻
자바 5이후 오토방식이 도입되고, 기본 타입과 참조 타입이 근본적으로 다름을 보장할 수 없게 되면서 문제가 발생했습니다.
public class AutoBoxing {
public void run() {
Set<Integer> set = new TreeSet<>();
List<Integer> list = new ArrayList<>();
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set + " " + list);
}
}
/* Result
[-3, -2, -1] [-2, 0, 2]
*/예시를 보면 이상하게 출력이 되는데 List<E> 인터페이스가 remove(Object) 와 remove(int)를 다중정의했기 때문입니다.
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove((Integer) i);
}
/* Result
[-3, -2, -1] [-3, -2, -1]
*/다음과 같이 Integer 를 매개변수로 넘겨주게 되면 정상적으로 원하는 결과를 얻을 수 있습니다.
3.3. 람다/메서드 참조와 다중정의
다중정의 메서드들(혹은 생성자)들이 함수형 인터페이스를 인수로 받을 때, 서로 다른 함수형 언티페이스라도 인수 위치가 같으면 혼란이 생깁니다. 달라 보이는 함수형 인터페이스도 근본적으로 다르지 않기 때문입니다.
아이템 53
1. 가변인수 메서드(Varargs Method)
- 명시한 타입의 인수를 0개 이상 받을 수 있다.
- 가변인수 메서드를 호출하면, 가장 먼저 인수의 개수와 길이가 같은 배열을 만들고 인수들을 이 배열에 저장하여 가변인수 메서드에 건네준다.
// 간단한 가변인수 활용 예
public int sum(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}
return sum;
}2. 가변인수 사용 방식
인수가 반드시 1개 이상이어야 할 때도 있습니다. 이러한 경우에 인수를 0개만 받을 수도 있도록 설계하는 건 좋지 않습니다.
public int getMinWithBadCase(int... args) {
if (args.length == 0) {
throw new RuntimeException("인수가 1개 이상 필요합니다."); // 인수가 0개이면 런타임에러
}
return Arrays.stream(args).min().getAsInt();
}위와 같은 설계와 구현을 지양해야 하므로 아래와 같이 구현을 변경할 수 있습니다.
public int getMinWithGoodCase(int arg, int... args) {
int min = arg;
for (int temp : args) {
min = Math.min(min, temp);
}
return min;
}위와 같이 수정하면 첫번째 인수는 반드시 필요하므로 1개 이상의 인수를 요청할 수 있습니다.
3. 가변인수 성능
가변인수 메서드는 호출될 때마다 배열을 새로 하나 할당하고 초기화합니다. 이 부분에서 최적화를 할 수 있는 부분이 있는데 바로 다중정의를 사용하는 방법입니다.
대표적인 예를 찾아보면 자바에서의 List.of() 메서드가 있습니다.
사진과 같이 수많은 메서드를 다중정의하여 구현하였습니다. 이렇게 하면 보통 때는 별 이득이 없지만, 인수가 적은 특수 상황에 오아시스가 되어줄 것 입니다.
🍃 결론
- 아이템 52: 프로그래밍 언어가 다중정의를 허용한다고 해서 다중정의 꼭 활용하란 뜻은 아닙니다! 일반적으로 매개변수 수가 같을 때는 다중정의를 피하는 게 좋다. 헷갈릴 만한 매개변수는 형변환하여 정확한 다중정의 메서드가 선택되도록 해야 합니다.
- 아이템 53: 인수 개수가 일정하지 않은 메서드를 정의해야 한다면 가변인수가 반드시 필요하며 메서드를 정의할 때 필수 매개변수는 가변인수 앞에 두고, 가변인수를 사용할 때는 성능 문제까지 고려합니다.
reference
- [함수] - 함수의 다중정의 ~ ThreeFive
- Effective Java