문자열은 널리 사용되며 자바에서 잘 지원되기 때문에, 일반적으로 텍스트 표현 이외의 다른 용도로도 자주 사용됩니다.
이번 아이템에서는 문자열을 쓰지 않아야 할 사례를 다루겠습니다.
- 문자열은 다른 값 타입을 대신하기에 적합하지 않음
- 문자열은 열거 타입을 대신하기에 적합하지 않음
- 문자열은 혼합 타입을 대신하기에 적합하지 않음
- 문자열은 권한을 표현하기에 적합하지 않음
1. 문자열은 다른 값 타입을 대신하기에 적합하지 않음
- 많은 개발자들이 파일, 네트워크, 키보드 입력으로부터 데이터를 받을 때 주로 문자열을 이용
- 하지만 입력받을 데이터가 진짜 문자열인 경우 즉, 텍스트일 때만 문자열을 사용하는 것을 권장
- 데이터가 수치형이면 int, long, double 등 수치에 적합한 타입을 사용
- `Yes/No`와 같은 질문의 답이라면 적절한 enum 타입 혹은 boolean을 사용
- 정리하자면 문자열은 텍스트 데이터를 다룰 때에만 사용하고, 다른 종류의 데이터는 적절한 값 타입을 사용하여 처리하는 것이 권장
2. 문자열은 열거 타입을 대신하기에 적합하지 않음
- 아이템 34에서 다루었다시피 상수를 열거할 경우에는 문자열 열거 패턴 클래스보다 enum 타입이 훨씬 나음
2.1 문자열 열거 패턴 클래스
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Day { | |
public static final String MONDAY = "월요일"; | |
public static final String TUESDAY = "화요일"; | |
public static final String WEDNESDAY = "수요일"; | |
// 나머지 요일들도 동일하게 정의 | |
} |
2.2 열거 타입
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public enum Day { | |
MONDAY("월요일"), | |
TUESDAY("화요일"), | |
WEDNESDAY("수요일"), | |
// 나머지 요일들도 동일하게 정의 | |
; | |
private final String value; | |
// 생성자를 이용하여 문자열 값을 할당 | |
Day(String value) { | |
this.value = value; | |
} | |
// 문자열 값을 반환하는 메서드 | |
public String getValue() { | |
return value; | |
} | |
} |
3. 문자열은 혼합 타입을 대신하기에 적합하지 않음
- 여러 요소가 혼합된 데이터를 하나의 문자열로 표현하는 것은 대체로 좋지 않음
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
String compoundKey = className + "#" + i.next(); |
코드 부연 설명
- i.next()에 구분자로 사용한 '#' 문자가 있는 경우 혼란스러운 결과가 발생
- 각 요소를 개별적으로 접근하기 위해서 특정 기준을 통해 문자열을 파싱해야 해서 느리고, 귀찮고, 오류 가능성도 큼
- 차라리 전용 클래스를 새로 만들어서 각 데이터 별로 멤버 변수를 취하는 것을 권장
4. 문자열은 권한을 표현하기에 적합하지 않음
- 다음과 같이 권한(capacity)를 문자열로 표현하는 경우가 종종 있음
4.1 문자열을 사용하여 권한을 구분한 잘 못된 예
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ThreadLocal { | |
private ThreadLocal() { | |
} //객체 생성 불가 | |
public static void set(String key, Object value); | |
public static Object get(String key); | |
} |
잘 못된 이유
- 쓰레드 구분용 문자열 키가 전역 변수에 공유됨
- 위 방식이 의도대로 동작하기 위해서는 각 클라이언트가 고유한 키를 제공해야 함
- 만약 ThreadLocal을 사용하는 클라이언트들끼리 합의가 되지 않은 상태로 동일한 키를 사용한다면 의도치 않게 같은 변수를 공유하게 되어 제대로 작동도 안 할뿐더러 보안에도 취약
4.2 별도의 타입으로 권한을 구분하여 해결한 예
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ThreadLocal { | |
private ThreadLocal() { | |
} //객체 생성 불가 | |
public static class Key { | |
key() { | |
} | |
} | |
// 위조 불가능한 고유 키를 생성한다. | |
public static Key getKey() { | |
return new Key(); | |
} | |
public static void set(Key key, Object value); | |
public static Object get(Key key); | |
} |
개선 포인트
- 앞서의 문자열 기반 API의 문제점을 해결해주지만 개선 포인트 존재
- set/get 메서드는 더 이상 정적 메서드일 필요가 없기 때문에 Key의 인스턴스 메서드로 변경하는 것을 권장
- 위와 같이 처리시 Key는 더 이상 쓰레드 지역변수를 구분하기 위한 Key가 아니라 그 자체가 쓰레드 지역 변수가 됨
4.3 개선된 예
결과적으로 지금의 top level 클래스인 ThreadLocal은 별달리 하는 일이 없어지므로 치워버리고, 중첩 클래스 Key 클래스명을 ThreadLocal로 변경했습니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ThreadLocal { | |
public ThreadLocal(); | |
public void set(Object value); | |
public Object get(); | |
} |
개선 포인트
- 위 API는 get으로 얻은 Object를 실제 타입으로 타입 캐스팅 해야하기 때문에 type-safe 하지 않음
4.4 최종 버전
제네릭을 사용하면 type-safe하게 처리 가능합니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ThreadLocal<T> { | |
public ThreadLocal(); | |
public void set(T value); | |
public T get(); | |
} |
정리
- 더 적합한 데이터 타입이 있거나 새로 작성할 수 있는 경우 문자열을 사용하지 말자
- 문자열을 잘 못 사용할 경우 번거롭고, 유연하지 않으며, 느리고 오류 발생 가능성이 크기 때문
참고
이펙티브 자바
반응형
'JAVA > Effective Java' 카테고리의 다른 글
[아이템 64] 객체는 인터페이스를 사용해 참조하라 (0) | 2024.03.18 |
---|---|
[아이템 63] 문자열 연결은 느리니 주의하라 (2) | 2024.03.17 |
[아이템 61] 박싱된 기본 타입보다는 기본 타입을 사용하라 (0) | 2024.03.17 |
[아이템 60] 정확한 답이 필요하다면 float와 double은 피하라 (0) | 2024.03.17 |
[아이템 59] 라이브러리를 익히고 사용하라 (0) | 2024.03.17 |