- 더 적합한 타입이 있다면 문자열을 쓰려는 유혹을 뿌리쳐라
- 잘못 사용하는 예시
- 값 타입을 대신할 용도
- 열거 타입을 대신할 요도
- 혼합 타입을 대신할 용도
- 권한을 나타내는 용도
- 데이터를 받을 때, 진짜 문자열일 때만 문자열을 사용하라
- 수치형일 때 > int, float, BigInteger
- 예/아니오 > boolean
적절한 값 타입이 있다면 그것을 사용하라
문자열 열거 패턴이라는 변형은 더 나쁘다
- 문자열 상수의 이름 대신 문자열 값을 그대로 하드 코딩
- 오타가 있어도 컴파일러는 확인할 길이 없어 런타임 버그
- 문자열 비교에 따른 성능 저하
그에 반해 열거 타입은 다음과 같은 장점이 있다
- 컴파일 타임 안전성을 보장한다
- 각자의 이름 공간이 있어 이름이 같은 상수도 평화롭게 공존한다. ex) 브로.콜리, 보더.콜리
- toString 메서드는 출력하기에 적합한 문자열을 내어준다
- 여러 요소가 혼합된 데이터를 하나의 문자열로 쓰지 마라
String compoundKey = className + "#" + i.next();
- 혼란의 여지가 있다 ex) 만약 복합키의 결과가 "#####" 였다면 어디까지가 className인가?
- 파싱과정이 느리고, 귀찮고 오류 가능성이 커진다
- equals, toString, compareTo를 제공하지 못하며 String 기능에 종속된다
ex) ThreadLocal : 스레드 지역변수 권한을 문자열로 표현하는 예시
ThreadLocal이란?
- 프로세스 : 실행 중인 하나의 어플리케이션
- 쓰레드 : 한 프로세스 내에서 동작되는 여러 실행 흐름
- 멀티 쓰레드 환경에서 자원을 공유하면서 발생하는 문제가 있다
- ThreadLocal은 오직 한 쓰레드에 의해 읽고 쓰여질 수 있는 변수를 의미한다
- 즉, Map<Thread, Object>의 형태로 현재 쓰레드를 공간으로 읽고 쓸 수 있는 변수를 저장하는 작업이다
public void set(T value) {
set(Thread.currentThread(), value);
if (TRACE_VTHREAD_LOCALS) {
dumpStackIfVirtualThread();
}
}
public T get() {
return get(Thread.currentThread());
}
- ThreadLocal 객체를 생성한다.
- ThreadLocal.set() 메서드를 이용해서 현재 쓰레드의 로컬 변수에 값을 저장한다.
- ThreadLocal.get() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 읽어온다.
- ThreadLocal.remove() 메서드를 이용해서 현재 쓰레드의 로컬 변수 값을 삭제한다
// 현재 쓰레드와 관련된 로컬 변수를 하나 생성한다.
ThreadLocal<UserInfo> local = new ThreadLocal<UserInfo>();
// 로컬 변수에 값 할당
local.set(currentUser);
// 이후 실행되는 코드는 쓰레드 로컬 변수 값을 사용
UserInfo userInfo = local.get();
reference)
https://javacan.tistory.com/entry/ThreadLocalUsage
public class ThreadLocal {
private ThreadLocal() {} // 객체 생성 불가
// 현 스레드의 값을 키로 구분해 저장
public static void set(String key, Object value);
// (키가 가리키는) 현 스레드의 값을 반환
public static Object get(String key);
}
문제점 : 키가 전역 이름 공간에서 공유된다
- 클라이언트의 키가 중복될 때 > 두 클라이언트 작업에 모두 오류가 발생한다
- 키 hijacking 위험이 있다 > 위조하거나, 갈취당할 수 있다
// 개선 1단계 : 위조 불가한 키 클래스 활용
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);
}
- 아직 개선의 여지가 있다
- set과 get이 static 메서드일 필요가 없다 > Key의 메서드로 편입하자
// 개선 2단계 : Key 내부로 set/ get 메서드 편입
public class ThreadLocal {
private ThreadLocal() {
} // 객체 생성 불가
public static class Key { //권한
Key() {
}
void set(Object value);
Object get();
}
//위조 불가한 고유 키를 생성한다.
public static Key getKey() {
return new Key();
}
}
- 이렇게 되면 Key가
지역변수 구분을 위한 키
가 아니라그 자체가 스레드 지역변수
가 된다. - 현재 ThreadLocal은 할일이 거의 없으므로 Key의 이름을 ThreadLocal로 바꾸자
// 개선 3단계 : 불필요한 톱레벨 클래스 제거
public class ThreadLocal {
public ThreadLocal() {}
public void set(Object value);
public Object get();
}
- 클라이언트가 Object를 실제 타입으로 형 변환하여 써야 한다
- 타입 안전성을 위해서 매개변수화 타입을 선언하여 마지막 개선을 끝내자
// 개선 4단계 : 타입 안전성을 위한 매개변수화 타입 활
public class ThreadLocal<T> {
public ThreadLocal() {}
public void set(T value);
public T get();
}