자바 언어·플랫폼 용어 사전
컬렉션오버로드

오버로드 해석

어떤 오버로드를 부를지 컴파일 시점에 정적 타입으로 결정하는 과정. List.remove(int)와 remove(Object)의 고전적 함정이 여기서 나온다.

실행 시점의 값이 아니라 컴파일 시점의 타입이 메서드를 고른다.

고전적인 사고

List<Integer> list = new ArrayList<>(List.of(10, 20, 30));

list.remove(1);                     // 정적 타입이 int → remove(int index)  → 인덱스 삭제
list.remove(Integer.valueOf(1));    // 정적 타입이 Integer → remove(Object) → 값 삭제

둘 다 정상 컴파일된다. 값을 지우려던 코드가 인덱스를 지우고, 인덱스 범위를 넘으면 엉뚱하게 IndexOutOfBoundsException 이 난다.

규칙은 세 단계다

1단계   박싱·가변인자 없이 맞는 것을 찾는다     ← remove(int) 가 여기서 잡힌다
2단계   박싱을 허용해 다시 찾는다
3단계   가변인자까지 허용해 찾는다

1단계에서 후보가 나오면 거기서 끝난다. int → Integer 박싱은 2단계라서 remove(int) 가 항상 이긴다. 자바가 하위 호환을 지키려고 만든 순서인데, 이 경우엔 직관과 반대로 작동한다.

제네릭이 도와주지 못한다

List<Integer> 라고 선언해도 소용없다. 타입 소거 이전에 시그니처가 두 개이기 때문이다. remove(Object) 는 제네릭과 무관하게 원래 Collection 인터페이스에 있던 메서드다.

피하는 법

  • 값 삭제는 removeIf(x -> x == 30) 또는 Integer.valueOf(...) 로 명시
  • 오버로드를 설계할 때 인자 개수가 같은 오버로드는 되도록 피한다(Effective Java Item 52)

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 컬렉션·예외·리소스