본문 바로가기
Programming/JAVA

[Effective Java] Primitive Type 과 Wrapper Class

by 코딩의성지 2023. 1. 30.

자바에는 여러 기본 자료형(Primitive Type) 과 해당 자료형에 대한 Wrapper Class를 제공한다. 우리는 이 Wrapper Class를 이용해 기본형을 객체로 생성해 이용할 수가 있다.

 

각 기본형에 해당하는 Wrapper Class 와 사용 방법은 아래와 같다.

 

기본형 Wrapper Class 예시
boolean Boolean Boolean a = new Boolean(true);
boolean b = new Boolean("false");
char Character Character a = new Character('a');
byte Byte Byte a = new Byte(3);
Byte b = new Byte("100");
short Short Short a = new Short(10);
Short b = new Short("1000");
int Integer Integer a = new Integer(10);
Integer b = new Integer("1000");
long Long Long a = new Long(10);
Long b = new Long("1000");
float Float Float a = new Float(12.12f);
Float b = new float("12.12f");
double Double Double a = new Double(12.12);
Double b = new Double("12.12");

Effective Java 에 따르면 Wrapper class 를 이용한 Boxing Type 을 이용하는 것 보다는 Primitive Type 사용을 권장한다.

 

아래 코드를 보자.

public static long sum() {
    Long sum = 0L;
    for(long i = 0 ; i<=Integer.MAX_VALUE; i++) {
        sum+= i;
    }
    return sum;
}
public static long sum() {
    long sum = 0L;
    for(long i = 0 ; i<=Integer.MAX_VALUE; i++) {
        sum+= i;
    }
    return sum;
}

0부터 Integer 의 최대 값까지 수를 더한 값을 리턴하는 간단한 메서드인데, 하나는 Boxing Type을 하나는 Primitive Type을 사용하고 있다. 실제로 해당 메서드를 수행해보면 Boxing Type 을 사용한 것 보다 Primitive Type 을 사용하는 것이 훨씬 빠르게 동작한다.

 

그 이유는 Long Type 의  sum 객체가 반복문이 한번 돌아갈 때마다 계속 생성되기 때문이다. 실제로 실무에서 리팩토링을 하다보면 레거시 코드에 이런 문제가 간혹 발견되곤 한다.

 

또한 Boxing Type을 남용하는 것을 주의하는 것도 중요하지만 , 위 코드처럼 의도하지 않은 Auto Boxing 도 주의해야한다.

 

하지만 무작정 기본형 타입만 고집하는 것도 답은 아니다. 간혹가다가 특정한 값을 null 로 처리해야할 경우가 있다. 예를 들어 특정 상품에 대한 가격을 산정하는데, 아직 정해지지 않았거나 무료인 상품에 대해서는 null로 DB에 저장하기로 한다면 , 어플리케이션 단에서 해당 상품에 대한 가격을 0이 아닌 null 로 넣어줘야하는데 이렬 경우 Wrapper Class 를 이용하여 null 로 처리할 수가 있다.

 

예제를 하나 더 살펴보자.

만약에 유효한 이메일 형식인지를 판별하는 메서드를 작성한다고 가정해보면 아래와 같이 작성할 수가 있다.

static boolean isEmailValid(String s) {
    return s.matches("\\w+@\\w+\\.\\w+(\\.\\w+)?");
}

위 메서드를 뜯어보면 어떠한 문제가 있음을 우리는 알 수 있다.

String 클래스의 matches 메서드를 실제로 확인해보면 아래와 같이 Pattern 클래스의 matches 메서드를 호출하여 리턴하도록 되어 있다.

public boolean matches(String expr) {
	return Pattern.matches(expr, this);
}

Pattern 클래스의 matches 메서드도 뜯어보자.

public static boolean matches(String regex, CharSequence input) {
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(input);
    return m.matches();
}

문제가 무엇인지 알겠는가? 우리는 유효한 이메일인지를 체크할 때마다 한번만 쓰고 말 패턴 객체를 계속해서 생성해버리고 있는 것이다.

실제로 실무에서 특정 파일의 확장자를 확인하는 메서드가 이런식으로 구현되어서 라이브환경에서 OOM 이 지속적으로 발생한 적 있기에 조심해야하는 부분이다.

 

그렇다면 이러한 로직은 어떻게 최적하는 것이 좋을까?  아래 코드를 보자.

private static final Pattern EMAIL = Pattern.compile("\\w+@\\w+\\.\\w+(\\.\\w+)?");

static boolean isEmailValid(String s) {
    return EMAIL.matcher(s).matches();
}

위와 같이 패턴 인스턴스가 매번 생성되지 않고 한번만 생성되도록 패턴 변수를 초기화 시켜놓는 방법을 사용할 수가 있다. 위와 같은 아주 간단한 방법으로 최적활 시킬 수가 있다.

 

여러분도 코딩할 때 이런부분도 고려해서 작업을 해보자.

 

Ref.

조슈아 블로크, 『이펙티브 자바』, 프로그래밍인사이트(2020), p31-p35

반응형

댓글