공공부하자개발 · 영어 학습 노트
자바
중급객체지향과 코어 라이브러리0/9 완료
  • 01상속, 다형성, 오버라이딩
  • 02추상 클래스 vs 인터페이스
  • 03예외 처리
  • 04java.lang 심화
  • 05컬렉션 프레임워크 딥다이브
  • 06메서드 활용 패턴 (중급)
  • 07Object 메서드와 비교
  • 08java.time 실무 날짜 계산
  • 09HTTP 와 JSON 기초
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 중급 › 06 / 9

메서드 활용 패턴 (중급)

섹션 6진행 0 / 9
1왜 배우는가2핵심 원리3패턴 모음4자주 하는 실수 (Tip)5연습 문제6정리‹ 이전다음 ›

2. 핵심 원리

2.1 생성자의 한계와 정적 팩토리

생성자는 이름이 클래스 이름으로 고정되어 있습니다. new Money(1000)이 원인지 달러인지, new Point(3, 4)가 직교좌표인지 극좌표인지 알 수 없습니다. 같은 시그니처의 생성자를 두 개 둘 수도 없습니다. 정적 팩토리 메서드(static factory method)는 이 한계를 넘습니다.

장점 예
이름을 가질 수 있다 Money.won(1000), Money.dollars(2)
호출할 때마다 새 객체를 만들지 않아도 된다 Boolean.valueOf(true), Integer.valueOf(127) — 캐시 반환
반환 타입의 하위 타입을 돌려줄 수 있다 List.of()는 List 인터페이스를 반환, 실제 클래스는 숨김
입력에 따라 다른 클래스를 돌려줄 수 있다 EnumSet.of()는 크기에 따라 RegularEnumSet 또는 JumboEnumSet

관례적 이름: of(값을 감싸기), from(다른 타입에서 변환), valueOf(of와 같지만 캐시 가능성), getInstance/newInstance, create. 생성자를 private으로 막고 정적 팩토리만 열어 두면 "만드는 방법"을 클래스가 완전히 통제합니다.

2.2 빌더 — 매개변수가 많을 때

매개변수가 4개를 넘고 그 중 일부가 선택 사항이면 생성자 오버로딩은 조합 폭발을 일으킵니다(점층적 생성자 패턴). 빌더(builder)는 필수 값은 빌더 생성자로, 선택 값은 이름 있는 메서드로 받아 build()에서 검증하고 객체를 만듭니다.

text
Order.builder("Kim")        ← 필수 인자
     .item("노트북")          ← 선택, return this
     .address("부산")         ← 선택, return this
     .build()               ← 검증 후 불변 Order 생성

return this가 체이닝을 가능하게 합니다. 각 setter가 자기 자신(빌더)을 돌려주므로 .을 계속 이어 쓸 수 있습니다.

2.3 디스패치 — 어떤 메서드가 실행되는가

종류 결정 시점 기준 예
오버로딩 컴파일 시점 인자의 정적 타입 print(1) vs print("a")
오버라이딩 런타임 객체의 실제 타입 Shape s = new Circle(); s.area() → Circle의 area
super.method() 컴파일 시점 부모 클래스의 구현으로 고정 오버라이딩한 메서드에서 부모 동작 재사용
default 메서드 런타임 구현 클래스가 덮어쓰지 않으면 인터페이스 것 Shape.describe()

템플릿 메서드가 동작하는 원리가 바로 런타임 디스패치입니다. 부모의 generate()가 header()를 호출하면, this의 실제 타입(CsvReport)의 header()가 실행됩니다. 부모는 자식이 누구인지 몰라도 됩니다.

2.4 반환값 설계 — 실패를 어떻게 알리는가

방법 언제 단점
null 반환 (권장하지 않음) 호출자가 잊으면 NPE
빈 컬렉션 반환 결과가 0개 이상인 목록 없음. 항상 이렇게
boolean 반환 성공/실패만 중요 실패 이유를 못 담음
Optional<T> 반환 있을 수도 없을 수도 있는 값 하나 필드·컬렉션 원소로는 쓰지 않음
예외 호출자가 복구할 수 없는 상황, 프로그래머 실수 흐름 제어용으로 남용하면 느리고 읽기 어려움
Result<T> 타입 실패가 정상 흐름의 일부이고 이유가 필요 직접 정의해야 함(sealed + record로 간단)

"찾는데 없을 수 있다"는 Optional, "입력이 잘못됐다"는 예외 또는 Result, "목록이 비었다"는 빈 컬렉션. 이 세 가지만 지켜도 API가 명확해집니다.

2.5 별칭(aliasing)과 방어적 복사

객체는 주소로 전달되므로, 생성자에서 받은 리스트를 그대로 필드에 넣으면 바깥 코드가 그 리스트를 바꿀 때 내 객체의 상태도 바뀝니다. 이를 별칭 문제라 합니다. 해결책은 두 방향의 복사입니다.

text
바깥 → 안 (생성자/setter):  this.items = new ArrayList<>(items);   방어적 복사
안 → 바깥 (getter):        return Collections.unmodifiableList(items);   불변 뷰
                            return List.copyOf(items);                   불변 복사

unmodifiableList는 원본이 바뀌면 같이 바뀌는 뷰이고, List.copyOf는 그 순간의 스냅샷입니다. 둘 다 바깥에서 add하면 UnsupportedOperationException입니다.

2.6 표준 메서드 4종의 계약

메서드 계약 어기면
equals 반사·대칭·추이·일관성, null과는 false List.contains, remove가 오동작
hashCode equals가 true면 반드시 같은 값 HashSet/HashMap에서 같은 값이 중복·분실
toString 사람이 읽을 수 있는 표현 디버깅·로그가 Money@1b6d
compareTo equals와 일관되게(권장), 부호가 뒤집혀야 함 TreeSet/sort 결과가 이상

record는 이 중 equals/hashCode/toString을 자동 생성합니다. 직접 클래스를 쓸 때는 Objects.equals, Objects.hash, Long.compare를 사용해 실수를 줄입니다.

핵심 원리
  • 2.1 생성자의 한계와 정적 팩토리
  • 2.2 빌더 — 매개변수가 많을 때
  • 2.3 디스패치 — 어떤 메서드가 실행되는가
  • 2.4 반환값 설계 — 실패를 어떻게 알리는가
  • 2.5 별칭(aliasing)과 방어적 복사
  • 2.6 표준 메서드 4종의 계약
이전 섹션1 왜 배우는가2 / 6다음 섹션3 패턴 모음