티스토리 뷰
728x90
반응형
다중정의는 신중하게 사용하라
- 다중정의란 이름이 같은 메서드가 매개변수 타입이나 개수만 다르게 갖는 형태를 다중정의(오버로딩)이라 합니다.
💡 다중 정의 메서드(Overload)
- 예상으로는 Set, List등 매개변수에 맞춰서 결과값이 출력될거라 생각했지만, 실제로 수행해보면 Collection 매개변수 메서드만 실행이 됩니다. 이유는 다중정의된 메서드 중 어떠한 메서드를 호출할지는 컴파일타임에 정해지기 때문입니다. 즉 현재 컴파일 시점으로 보면 for(Collection<?> c : collections)로 Collection 타입이기 때문에 Collection 매개변수를 갖는 메서드가 실행되었습니다.
- 런타임에는 타입이 매번 달라지겠지만 호출할 메서드를 선택하는데는 아무런 영향을 미치지 못합니다. 따라서 컴파일 시점에 이미 실행될 메서드가 지정되어 있으므로 3번 연속으로 Collection 매개변수가 호출됩니다.
- 이처럼 직관과 어긋나는 이유는 재정의한 메서드는 동적(런타임)으로 선택되고, 다중정의한 메서드는 정적(컴파일)으로 선택되기 때문입니다.
- 다중정의된 메서드 사이에서는 객체의 런타임 타입은 전혀 중요치 않습니다. 선택은 컴파일타임에, 오직 매개변수의 컴파일타임 타입에 의해 이루어집니다.
public class CollectionClassifier {
public static String method(Object o) {
return "Object 메서드";
}
public static String method(Set<?> set) {
return "Set 매개변수 메서드";
}
public static String method(List<?> list) {
return "List 매개변수 메서드";
}
public static String method(Collection<?> collection) {
return "Collection 매개변수 메서드";
}
}
public class Example {
public static void main(String[] args) {
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<Integer>(),
new HashMap<String, String>().values()
};
for (Collection<?> c: collections) {
System.out.println(CollectionClassifier.method(c));
}
}
}
💡 재정의(Override)
- 아래 예제를 실행하면 포도주, 발포성 포도주, 샴페인이 차례대로 출력됩니다. for문에서 컴파일타임 타입이 모두 Wine이지만 상속을 통해 재정의한 메서드가 실행이 됩니다.
public class Wine {
String getName() {
return "포도주";
}
}
class SparklingWine extends Wine {
@Override
String getName() {
return "발포성 포도주";
}
}
class Champagne extends SparklingWine {
@Override
String getName() {
return "샴페인";
}
}
public class Example {
public static void main(String[] args) {
List<Wine> wines = List.of(
new Wine(), new SparklingWine(), new Champagne()
);
for (Wine wine : wines) {
System.out.println(wine.getName());
}
}
}
💡 다중정의가 일으킬 수 있는 혼동을 피하자
- 프로그래머에게는 재정의가 정상적인 동작방식이고, 다중정의가 예외적인 동작으로 보일 것입니다. 즉 재정의한 메서드는 프로그래머가 기대한 대로 동작하지만 다중정의한 메서드는 이러한 기대를 가볍게 무시합니다. 그렇기 때문에 다중정의가 혼동을 일으키는 상황을 피해야 합니다.
- 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말아야 합니다.
- 다중정의하는 대신 메서드 이름을 다르게 지어주는 길도 항상 열려있습니다.
💡 다중정의가 필요하다면?
- 메서드 이름을 다르게 지어 메서드명을 통해 명시적으로 클라이언트에게 노출하는 방법이 있습니다.
- ObjectOutputStream 클래스의 writeXXX 메서드를 예로 들 수 있습니다.
- 모두 같은 매개변수 개수를 가집니다. 하지만 다중정의가 아닌 네이밍을 통해 메서드의 동작을 예상할 수 있게해줍니다.
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants {
public void writeBoolean(boolean val) throws IOException {
bout.writeBoolean(val);
}
public void writeByte(int val) throws IOException {
bout.writeByte(val);
}
public void writeShort(int val) throws IOException {
bout.writeShort(val);
}
public void writeChar(int val) throws IOException {
bout.writeChar(val);
}
// ...
}
💡 안전한 다중정의
- 매개변수 수가 같은 다중정의 메서드가 많더라도, 그중 어느것이 주어진 매개변수 집합을 처리할지가 명확하게 구분된다면 헷갈릴 일이 없을 것입니다. 즉, 매개변수 중 하나 이상의 값이 근본적으로 다르다면 헷갈릴 일이 없습니다. 근본적으로 다르다는 건 두 타입의 값을 서로 어느쪽으로든 형변환할 수 없다는 의미입니다.
- 이 조건만 충족하면 어느 다중정의 메서드를 호출할지가 매개변수둘의 런타임 타입만으로 결정됩니다. 따라서 컴파일시점에 영향을 받지 않게되고 혼란을 줄일 수 있습니다.
- 예를들어 ArrayList에는 int를 받는 생성자와 Collection을 받는 생성자가 있는데, 어떤 상황에서든 두 생성자 중 어느것이 호출될지 헷갈릴 일은 없습니다.
List<String> strings1 = new ArrayList<>(20);
Set<String> set = new HashSet<>();
set.add("Hello");
List<String> strings2 = new ArrayList<>(set);
- ArrayList 클래스의 생성자
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
}
💡 Autoboxing의 등장으로 인한 주의사항
- 자바 5에 등장한 오토박싱으로 인한 문제가 발생할 여지가 생겼습니다.
- 다음은 List 인터페이스의 remove API입니다.
public interface List<E> extends Collection<E> {
boolean remove(Object o);
E remove(int index);
}
- List 인터페이스에 remove 메서드가 다중정의되어 있는데 아래 예제 코드에서 remove 메서드를 호출하면 어떻게 될까요?
- 제가 지우고자하는 값은 1입니다. 하지만 반환값은 [1, 3, 4, 5]를 반환하고 있습니다.
그 이유는 List 인터페이스 중 E remove(int index) 메서드가 선택되었기 때문입니다. 이 메서드를 사용하면 매개변수를 인덱스로 사용하기 때문에 1이 아닌 2가 삭제된 것입니다.
public class Example {
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>(List.of(1, 2, 3, 4, 5));
integers.remove(1);
System.out.println(integers); // [1, 3, 4, 5]
}
}
- 아래처럼 해야 의도하는 값을 얻을 수 있습니다.
public class Example {
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>(List.of(1, 2, 3, 4, 5));
integers.remove((Integer) 1);
System.out.println(integers); // [2, 3, 4, 5]
}
}
✔️ 정리
- 다중정의(오버로딩)된 메서드 중 어떤 메서드가 호출될지는 컴파일 시점에 정해집니다.
- 재정의(오버라이딩)한 메서드는 동적(런타임)으로 선택되고, 다중정의한 메서드는 정적(컴파일)으로 선택됩니다.
- 안전하고 보수적으로 갈려면 매개변수 수가 같은 다중정의는 만들지 말아야 합니다.
- 매개변수가 하나 이상 근본적으로 다른 경우라면 혼동되지 않을 수 있습니다. (ArrayList 클래스의 생성자)
- 매개변수로 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안됩니다.
- 참조 메서드와 호출 메서드가 모두 다중정의 되어있을 경우 다중정의 해소 알고리즘이 제대로 동작하지 않을 수 있습니다.
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item54. null이 아닌, 빈 컬렉션이나 배열을 반환하라 (0) | 2022.08.06 |
---|---|
이펙티브 자바 - Item53. 가변인수는 신중히 사용하라 (0) | 2022.08.06 |
이펙티브 자바 - Item51. 메서드 시그니처를 신중히 설계하라 (0) | 2022.08.04 |
이펙티브 자바 - Item50. 적시에 방어적 복사본을 만들라 (0) | 2022.08.03 |
이펙티브 자바 - Item49. 매개변수가 유효한지 검사하라 (0) | 2022.08.02 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- spring boot redis 대기열 구현
- spring boot redisson sorted set
- polling publisher spring boot
- java ThreadLocal
- spring boot 엑셀 다운로드
- transactional outbox pattern spring boot
- spring boot excel download oom
- spring boot redisson destributed lock
- spring boot poi excel download
- microkernel architecture
- 트랜잭셔널 아웃박스 패턴 스프링부트
- 자바 백엔드 개발자 추천 도서
- spring boot redisson 분산락 구현
- transactional outbox pattern
- redis sorted set
- pipe and filter architecture
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- JDK Dynamic Proxy와 CGLIB의 차이
- redis 대기열 구현
- 공간 기반 아키텍처
- spring boot excel download paging
- java userThread와 DaemonThread
- service based architecture
- 람다 표현식
- @ControllerAdvice
- 레이어드 아키텍처란
- 서비스 기반 아키텍처
- space based architecture
- pipeline architecture
- redis sorted set으로 대기열 구현
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함