Clean Code that Works.

이번에도 읽어 볼만한 글을 간단하게 번역해봤습니다.

http://javarevisited.blogspot.in/2014/05/how-string-in-switch-works-in-java-7.html


Java 7의 특징중에 하나로 Switch 구문에서 문자열을 사용할 수 있는데요.

이 원리에 대해서 설명한 내용입니다. :)


3줄 요약하면

1. Switch 에서 문자열을 사용했을 경우에 컴파일된 코드를 보면 hash code 와 equals() 를 사용해서 구분을 한것을 알 수 있다.

2. equals()를 사용해서 값을 확인을 한번 더 하기 때문에 숫자형이나 열거형을 사용했을경우 보다 연산이 +1 되고, 대소문자 문제 및 카멜표기법에 따른 문제 등이 발생한다.

3. 그러므로 문자열 보다는 숫자형이나 열거형을 사용하는 것이 좋고 열거형이 가독성과 코드 품질에 더 좋기 때문에 열거형을 사용하는 것이 좋다.


가볍게 읽어보세요 


------------------------------------------------------------------------------------------------------------------------------------------------


Java의 switch 구문에서 문자열을 사용할 수 있게 됨으로써 숫자형이나 열거형을 쓰는 것 보다 더 사용하기가 쉬어서 많은 프로그래머들이 이를 사용한다. 이 기능은 자동 리소스 관리 및 다중 오류 catch blocks 를 포함해서 JDK 7의 기능중에 가장 유명한 기능중 하나다. 


Java 프로그램에서 문자열을 사용함으로써 주는 한가지 편의점은 간단하게 사용할 수 있다는 것이다. 하지만 새로운 내용에 대한것을 실제 제품 코드에 적용하기전에 나는  이 내용에 대해 조금 더 알아보는것을 선호 한다. 내가 이 기능에 대해서 처음 알았을 때 equals() 와 hashcode()를 사용하면 switch에서 문자열 사용하기를 구현할 수 있을 거라는 생각이 들었다. 그래서 Java 7 에서 문자열이 어떻게 Switch 에서 사용할 수 있게 됬는지 관심을 가지게 됬다. 한가지 더 이슈는 Java 면접을 진행할 때 질문을 하기 위해서 였고, 이런 질문을 가지고 있는 면접이 좀 더 흥미롭게 진행 될 수 있다.(간단하지만 흥미로운 질문이긴 하네요)


테스트는 간단하다. 그냥 코드를 Switch 블럭에서 문자열로 작성하면 되고 이 코드들을 디컴파일 해서 컴파일러가 이 코드들을 어떻게 변환했는지 보면 된다. 


파일되기 전 코드

아래 코드는 main 메서드에 switch 블럭을 가지고 있고 문자열을 포함한 간단한 테스트 코드다. String 변수가 프로그램이 동작할시 제공된다. 아래 테스트 코드는 3가지 모드(Activce, Passive, Safe)를 가지고 있다. 


고정값을 사용할 경우에는 열거형을 사용하는게 더 좋지만 문자열을 사용하기로 결정했다면 소문자 및 카멜 표기법으로 변수를 표시했을때 문제가 발생할 수 있으므로 대문자로 작성 하도록 한다. 


/**

* Java Program to demonstrate how string in switch functionality is implemented in * Java SE 7 release. 

*/ 

public class StringInSwitchCase 

public static void main(String[] args) { 

String mode = args[0]; 

switch (mode) { 

case "ACTIVE": 

System.out.println("Application is running on Active mode"); 

break

case "PASSIVE": 

System.out.println("Application is running on Passive mode"); 

break

case "SAFE": 

System.out.println("Application is running on Safe mode"); 

}

}


JDK 1.7을 설치해서 컴파일하고 코드를 실행해본다. JDK 7 어느 버전에서든 동작 한다.


디컴파일 된 코드

아래 코드는 jdk 1.7.0_40 버전에서 컴파일된 소스를 디컴파일 한 것이다. 만약 당신이 자바를 처음 접하고 자바 클래스파일을 디컴파일에 리버스 엔지니어링을 어떻게 하는지 알고 싶다면 이 게시글을 참조하라. 


새로운 버전이 계속 나오면서 문섭들이 점점 더 편리해지고 쉬어 지기 때문(설탕 문법, syntactic sugar)에 이를 확실하게 알려면 모든 레벨의 자바 프로그래머들에게 클래스를 디컴파일하는 방법을 아는것은 매우 중요하다. 


/** * Reverse Engineered code to show how String in Switch works in Java. */ 

import java.io.PrintStream


public class StringInSwitchCase

public StringInSwitchCase() { } 

public static void main(string args[]) { 

String mode = args[0]; 

String s; 

switch ((s = mode).hashCode()) { 

default: break

case -74056953: 

if (s.equals("PASSIVE")) { 

System.out.println("Application is running on Passive mode"); 

}

break

case 2537357: 

if (s.equals("SAFE")) { 

System.out.println("Application is running on Safe mode"); 

} 

break

case 1925346054: 

if (s.equals("ACTIVE")) { 

System.out.println("Application is running on Active mode"); 

break

}

}


이 코드에서 문자열이 hashCode()와 equals() 메서드를 사용하는걸 볼 수 있다. 

JAVA 7 이전에 swich 구문에서는 byte, short, car 및 int 만 사용가능했던걸 되세겨보자. 


코드를 자세히 들여다 보면 swich는 해쉬 코드 와 eqauls()메서드를 통한 비교를 하고 있다. 이 검사는 꼭 필요한데 두개의 유니크한 오브젝트도 같은 hashcode 값을 가질 수 있다. 성능으로 따지면 열거형을 사용하는 것이나 숫자형을 사용하는 것 만큼 빠르지 않지만 모든 부분에서 아주 좋지 않은것은 아니다. 

자바 컴파일러는 문자열을 비교할때 매우 빠르게 할 수 있는 equals()를 사용한다.("abc" == "abc"). hashcode()메서드를 사용하는 경우에는 코드가 이미 생성되어 있기 때문에 1번더 추가적인 시간이 소모된다. 문자열은 그들의 hash code를 캐시한다. 그래서 이 코드가 hash code를 호출하는 비용은 크지 않을 것이다. 

그럼에도 불구하고 나는 여전히 Switch 에서 문자열을 사용하거나 고정된 숫자값을 사용하는것은 좋은 방법이 아니라고 본다. 자바는 이러한 이유때문에 열거형이라는 타입을 제공하고 모든 자바 프로그래머는 되도록 열거형을 사용하는 것이 좋다.


이게 Java 7의 Switch 구문에 문자열이 어떻게 동작하는지에 대한 것이다. 

예상했던 대로 전환을 하기 위해 hashCode()를 사용하고 검증을 하기 위해서 equals()를 사용한다. 이것은 문법적으로는 심플 하지만 오히려 기본 성능보단 느리다. 


결국 선택은 사용자의 몫이다. 나는 개인적으로 Switch 구문에서 문자열을 사용하는것(다루기 힘든 코드, 대소문자 문제, 값 검증을 위한 컴파일 시간 문제)에 대해 좋아하는 사람은 아니다.


사실 숫자 정수와 열거형 타입은 성능이 중요한 코드에서 내가 좋아 하는 방법이다. 가독성과 코드 품질이 더 중요하다.

실제로는 99.99%로 열거형을 쓰는게 문자열이나 숫자형을 사용하는 것보다 좋다.