빌드노트
Boolean.getBoolean("true")가 false를 반환할 때
Boolean.getBoolean은 문자열 값이 아니라 JVM 시스템 속성 이름을 받는다. 속성 없음과 -Dlab.flag=true/false를 별도 JVM에서 비교하고, parseBoolean으로 바꿀 경우와 속성 원문을 확인할 경우를 구분한다.
설정에서 읽은 문자열이 "true"인데 Boolean.getBoolean(value)가 false를 반환한다면, 호출에 넘긴 것이 값인지 이름인지 확인한다. getBoolean("true")는 문자열 "true"를 변환하지 않고, 이름이 true인 JVM 시스템 속성을 찾는다.
이미 읽어 온 설정 문자열을 변환하려면 Boolean.parseBoolean(value)를 쓴다. 반대로 JVM을 실행할 때 지정한 lab.flag 속성을 읽으려는 코드라면 Boolean.getBoolean("lab.flag")가 맞다. 두 메서드 모두 String을 받지만 그 문자열이 가리키는 대상이 다르다.
값은 parseBoolean, 속성 이름은 getBoolean
Java SE 25 Boolean API (새 탭에서 열림)는 parseBoolean이 인자 자체를 해석하고, getBoolean은 인자와 같은 이름의 시스템 속성 값을 확인한다고 설명한다. getBoolean은 그 속성이 존재하고 값이 대소문자를 무시한 "true"일 때만 true를 반환한다. 속성이 없으면 false다.
따라서 getBoolean("true")가 언제나 false인 것은 아니다. 이름이 true인 속성에 값 "true"가 들어 있다면 문서상 결과는 true다. 자기 코드에서는 설정값을 속성 이름 자리에 넣고 있는지를 확인해야 한다.
같은 소스에 실행 옵션만 바꿔 보기
아래 코드는 속성의 원문과 두 API의 결과를 함께 보여 준다. JDK 17의 java 명령을 사용할 수 있는 터미널에서 새 폴더에 FlagLab.java라는 이름으로 저장한다. 이번 실행 환경은 Windows 11, PowerShell 7.6.6, OpenJDK 17.0.2였다. Java 실행기 문서 (새 탭에서 열림)에 설명된 소스 파일 실행 방식을 사용하므로 별도의 컴파일 명령은 필요 없다.
class FlagLab {
public static void main(String[] args) {
System.out.println("property.true=" + System.getProperty("true"));
System.out.println("property.lab.flag=" + System.getProperty("lab.flag"));
System.out.println("getBoolean.true=" + Boolean.getBoolean("true"));
System.out.println("getBoolean.lab.flag=" + Boolean.getBoolean("lab.flag"));
System.out.println("parseBoolean.true=" + Boolean.parseBoolean("true"));
}
}
파일을 저장한 폴더에서 java FlagLab.java를 실행하면, 옵션으로 속성을 지정하지 않은 JVM의 상태를 확인할 수 있다. 실제 출력은 다음과 같았다.
property.true=null
property.lab.flag=null
getBoolean.true=false
getBoolean.lab.flag=false
parseBoolean.true=true
앞의 두 줄에서 null은 속성이 없다는 뜻이다. 같은 "true"를 넘겼는데도 getBoolean은 없는 속성을 조회해 false, parseBoolean은 문자열 값을 해석해 true를 반환했다. 자기 환경에서 첫 줄이 null이 아니라면 이미 그 이름의 속성이 있는 것이므로 이 출력과 조건이 다르다.
이번에는 같은 lab.flag 속성의 값만 바꿔 두 번 실행한다. -D이름=값은 해당 JVM의 시스템 속성을 지정하는 실행 옵션이며, 소스 파일 이름 앞에 둔다. 다음은 실제 실행한 PowerShell 명령이다.
java "-Dlab.flag=true" FlagLab.java
java "-Dlab.flag=false" FlagLab.java
첫 실행에서는 property.lab.flag=true, getBoolean.lab.flag=true가 나왔다. 두 번째 실행에서는 두 줄이 각각 property.lab.flag=false, getBoolean.lab.flag=false로 바뀌었다. 두 실행 모두 property.true=null, getBoolean.true=false, parseBoolean.true=true는 그대로였다. lab.flag를 지정해도 getBoolean("true")가 읽는 이름은 여전히 true이기 때문이다.
lab.flag의 원문이 null로 나온다면 그 JVM에 옵션이 전달되지 않은 것이다. -D 옵션을 소스 파일 이름 뒤에 썼는지 확인한다. 실행기 문서에 따르면 소스 파일 이름 뒤의 인자는 프로그램의 main 인자가 된다. 실제 앱에 적용할 때도 앱이 실행되는 JVM의 옵션 위치를 확인해야 한다.
false만 보면 누락과 꺼짐을 구별할 수 없다
위 비교에서 속성이 없는 경우와 값이 "false"인 경우는 모두 getBoolean("lab.flag")가 false였다. 추가 실행에서는 값 "TRUE"가 true, "yes"가 false로 판정됐다. false가 나왔다고 해서 올바른 설정 이름을 찾았거나, 사용자가 명시적으로 기능을 껐다고 판단할 수는 없다.
진단할 때는 예제처럼 System.getProperty("lab.flag")로 원문을 함께 확인한다. null이면 속성 이름과 JVM 실행 옵션부터 보고, 문자열 "false"면 설정된 값을 확인한다. 누락이나 잘못된 단어를 설정 오류로 처리해야 하는 프로그램이라면, 불리언 변환에 앞서 허용할 입력을 따로 검사해야 한다.
문자열 파싱도 입력 검증을 대신하지 않는다. Java SE 17 Boolean API (새 탭에서 열림)에 따르면 parseBoolean은 null이 아니고 대소문자를 무시해 "true"와 같은 문자열에만 true를 반환하고 나머지는 false로 처리한다. 설정 파일이나 외부 입력에서 이미 얻은 값은 이 메서드로 변환하되, 잘못된 값을 조용히 꺼짐으로 처리해도 되는지는 별도로 정한다.
직접 실행한 범위는 OpenJDK 17.0.2의 별도 JVM과 합성 속성 lab.flag다. 옵션 없는 JVM을 마지막에 다시 실행했을 때도 속성은 없었다. Java SE 25 문서의 두 API 계약도 확인했지만 JDK 25에서는 실행하지 않았으며, 실제 앱이나 설정 프레임워크가 값을 읽고 전달하는 과정은 검사하지 않았다.