조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.
1. 다중정의는 신중히 사용하라
다중정의한 메서드는 정적으로 선택됨
오버로딩의 호출 메서드는 컴파일 타임에 정해짐
public class CollectionClassifier {
public static String classify(Set<?> s) { return "set"; }
public static String classify(List<?> list) { return "list"; }
public static String classify(Collection<?> c) { return "collection"; }
public static void main(String[] args) {
CollectionClassifier classifier = new CollectionClassifier();
Collection<?>[] list = new {new HashSet<>(), new ArrayList<>()};
for (Collection<?> c : list) {
System.out.println(classifier.classify(c); // collection
}
}
}
- 런타임에는 매번 타입이 달라지지만 호출할 메서드를 결정하지 못함
해결책
public class CollectionClassifier {
public static String classify(Collection<?> c) {
return c instanceof Set ? "set" :
c instanceof List ? "list" : "collection";
}
public static void main(String[] args) {
CollectionClassifier classifier = new CollectionClassifier();
Collection<?>[] list = new {new HashSet<>(), new ArrayList<>()};
for (Collection<?> c : list) {
System.out.println(classifier.classify(c);
}
// set
// list
}
}
- instanceof 활용
혼동을 피하기 위해 매개변수 수가 같은 다중정의는 만들지 말자
정적 팩터리 사용하기 (생성자)
메서드 이름을 다르게 짓기
public class ObjectOutputStream
extends OutputStream implements ObjectOutput, ObjectStreamConstants {
public void write(byte[] buf) throws IOException {
bout.write(buf, 0, buf.length, false);
}
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);
}
}
타입이 서로 연관되지 않다면, 매개변수 수가 같은 다중정의는 런타임만 신경쓰면 됨
ArrayList(int), ArrayList(Collection)
- 인자의 타입이 서로 연관이 없어 혼동이 일어나지 않음
타입이 서로 연관되어 있다면, 매개변수 수가 같은 다중정의는 구체 타입으로 호출됨
Collection.remove(Object), Collection.remove(Integer)
public class OverloadingExample {
public static void remove(Integer i) {
System.out.println("remove(Integer) called with: " + i);
}
public static void remove(Object o) {
System.out.println("remove(Object) called with: " + o);
}
public static void main(String[] args) {
int number = 5;
remove(number); // int -> Integer (autoboxing) -> remove(Integer) 호출
}
}
- int 값이 온다면, 더 구체적인 Collection.remove(Integer)가 호출됨
서로 다른 함수형 인터페이스 더라도 같은 위치의 인수를 받아서는 안됨
ExecutorService.submit
ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(System.out::println);
- 다중정의 메서드를 메서드 참조 시, 부정확하게 참조될 수 있음
- Runnable, Callable<T> 를 받을 수 있음
- System.out.println()은 여러 시그니처를 가지고 있으므로 현재 정보로는 타입 추론이 어려움
- 어느 다중정의로 구현되는지 알 수 없어 예외가 발생함
해결책: 명시적으로 전달하기
exec.submit(() -> System.out.println());
exec.submit((Runnable) System.out::println);
2. 가변인수는 신중히 사용하라
가변 인수
- 명시한 타입의 인수를 0개 이상 받을 수 있음
- 인수의 개수와 길이가 같은 배열을 만들고 이 인수들을 배열에 저장하여 가변인수 메서드에 건네줌
예외 상황
인수가 1개 이상을 강제해야 하는 메서드
- 메서드 몸체 내에서 런타임 에러가 발생함
- 이를 위해 유효성 검사를 명시적으로 해야 함
해결책: 매개변수 2개 받기
static int min(int firstArg, int... remainingArgs) {}
- 평범함 매개변수 받기
- 가변인수 매개변수 받기
3. null이 아닌, 빈 컬렉션이나 배열을 반환하라
null을 반환하면 안되는 이유
사용하기 어려움
List<String> result = getList();
if (result != null) {
for (String item : result) {
System.out.println(item);
}
}
- 호출하는 쪽에서 항상 null check 해야 함
오류 가능성 증가
List<String> result = getList();
for (String item : result) { // NPE 가능성 있음
System.out.println(item);
}
- null check 누락 시, NPE 발생
빈 불변 컬렉션 반환
Collections.emptyList() 활용
- 수정 불가
- 읽기 전용
3. 옵셔널 반환은 신중히 하라
Optional<T>
- 값이 존재할 수도, 존재하지 않을수도 있는 상황에서 안전하게 처리하기 위한 컨테이너
- null을 직접 다루는 대신, 값이 없을 가능성을 명시적으로 처리할 수 있음
- T 타입 참조 (not null) or 아무것도 담지 않음
- 불변 컬렉션
주요 메서드
get()
Optional<String> optional = Optional.of("Hello");
String value = optional.get(); // "Hello"
- 값이 존재하면 받아올 수 있음
- 값이 없을 경우 사용 시, NoSuchElementException 발생
orElse("기본값")
Optional<String> optional = Optional.empty();
String value = optional.orElse("Default Value"); // "Default Value"
- 값이 없을 경우 기본값 설정
orElseGet(Supplier)
Optional<String> optional = Optional.empty();
String value = optional.orElseGet(() -> "Generated Value"); // "Generated Value"
- 값이 없을 경우, Supplier에서 제공한 값 반환
- 예외 시에 발생하므로, 지연 연산이 가능함
orElseThrow(Supplier)
Optional<String> optional = Optional.empty();
String value = optional.orElseThrow(() -> new IllegalArgumentException("No value present"));
// throws IllegalArgumentException
- 값이 없을 경우, 지정된 예외 던짐
주의사항
컬렉션은 옵셔널에 담으면 안됨
래퍼타입은 옵셔널에 담으면 안됨
- Optional은 단일 값에만 의미가 있음
'Java' 카테고리의 다른 글
[Effective Java] 10-1. 예외 (0) | 2025.01.01 |
---|---|
[Effective Java] 9-1. 일반적인 프로그래밍 원칙 (0) | 2025.01.01 |
[Effective Java] 8-1. 메서드 (2) | 2024.12.31 |
[Effective Java] 7-3. 람다와 스트림 (0) | 2024.12.31 |
[Effective Java] 7-2. 람다와 스트림 (0) | 2024.12.31 |