Java boolean에서 &와 &&는 같은 진리표 결과를 냅니다. 하지만 &는 양쪽 피연산자를 평가하고, &&는 왼쪽 값이 false이면 오른쪽 식을 실행하지 않습니다.
그래서 오른쪽 식에 메서드 호출이나 상태 변화가 있으면 결과값은 같아도 실행 횟수는 달라질 수 있습니다. 여기서는 숫자 비트 연산이 아니라 두 피연산자가 boolean인 경우를 다룹니다.
진리값은 같아도 오른쪽 실행 여부는 다릅니다
Java에서 boolean 값에 &를 적용하면 두 값이 모두 true일 때만 결과가 true입니다. 조건 AND 연산자인 &&도 두 값이 모두 true일 때 true를 돌려줍니다. 따라서 입력값만 놓고 표를 그리면 둘의 진리값은 같습니다. 차이는 실행 과정에 있습니다.
JLS 21은 &&의 왼쪽 피연산자를 먼저 평가하고, 그 값이 false이면 오른쪽 피연산자를 평가하지 않는다고 규정합니다. 반면 boolean &는 일반 & 연산자로 양쪽 피연산자의 값을 계산합니다. 오른쪽에 메서드 호출이 들어 있으면 이 차이가 호출 횟수로 드러납니다. 결과 boolean만 적으면 부수 효과를 놓칠 수 있습니다.
public class BooleanEvaluation {
static int calls;
static boolean check() {
calls++;
return true;
}
public static void main(String[] args) {
calls = 0;
boolean andResult = false & check();
System.out.println(andResult + " calls=" + calls);
calls = 0;
boolean shortCircuitResult = false && check();
System.out.println(shortCircuitResult + " calls=" + calls);
}
}false &는 호출을 세고 false &&는 건너뜁니다
첫 식은 false & check()입니다. 왼쪽 값 false만으로 최종 결과가 false라는 점은 알 수 있지만, &의 오른쪽 check()도 평가됩니다. 메서드가 호출 횟수를 1 올리고 true를 돌려주므로 결과는 false이고 호출 횟수는 1입니다.
두 번째 식은 false && check()입니다. 왼쪽 피연산자를 먼저 읽은 뒤 false를 얻습니다. 조건 AND는 이 경우 최종 결과가 false로 확정되므로 오른쪽을 평가하지 않습니다. 호출 횟수는 0입니다. 두 결과값은 모두 false이지만, 메서드 실행 횟수는 각각 1과 0입니다. 코드 추적 문제에서 함수 호출이나 변수 변경이 들어 있으면 결과와 부수 효과를 각각 적어야 하는 이유입니다.
| 식 | 왼쪽 값 | 오른쪽 호출 | 결과 | calls |
|---|---|---|---|---|
| false & check() | false | 실행 | false | 1 |
| false && check() | false | 건너뜀 | false | 0 |
| true & check() | true | 실행 | true | 1 |
| true && check() | true | 실행 | true | 1 |
왼쪽이 true인 마지막 두 경우에는 두 연산자 모두 오른쪽을 평가합니다. &&는 오른쪽을 항상 건너뛰는 연산자가 아닙니다. 왼쪽 값에 따라 필요할 때만 평가합니다. 오른쪽 결과가 false라면 전체 결과도 false가 됩니다. 즉 short-circuit 여부는 왼쪽이 결정 결과를 이미 확정했는지로 판단합니다.
식의 결과와 호출 수를 두 번 추적합니다
- 피연산자 타입이
boolean인지 확인해 정수 비트 연산과 구분합니다. - 왼쪽 피연산자의 값을 먼저 적습니다.
- 연산자가
&이면 오른쪽도 평가하고,&&이면 왼쪽 false에서 종료합니다. - 실행한 메서드의 반환값으로 전체 boolean 결과를 계산합니다.
- 메서드 호출, 카운터 변경, 출력처럼 관찰 가능한 부수 효과를 별도 열에 적습니다.
문제에서 if (isReady() && load())처럼 메서드 이름이 나오면 호출 순서를 먼저 표시해 보세요. isReady()가 false를 돌려주면 load()는 실행되지 않습니다. 만약 첫 연산자가 boolean &라면 load()도 호출됩니다. 양쪽 메서드가 매번 같은 결과만 돌려준다고 해도 호출 여부는 외부 상태, 로그, 시간 소모에 영향을 줄 수 있으므로 의미상 같다고 단정할 수 없습니다.
숫자 비트 AND와 boolean 조건 AND를 혼동하지 않습니다
흔한 실수는 &가 언제나 단락 평가한다고 생각하거나, 반대로 boolean &와 정수 비트 &를 같은 결과표 하나로 처리하는 것입니다. 정수 피연산자에서는 &가 각 비트에 AND를 적용합니다. boolean 피연산자에서는 논리 AND 결과를 만들지만 두 피연산자는 계산됩니다. Java에는 조건 AND인 &&가 따로 있고, 이 연산자만 왼쪽이 false일 때 오른쪽 평가를 생략합니다.
또 다른 실수는 결과가 같으므로 두 식을 서로 바꾸어도 프로그램이 같다고 보는 것입니다. 오른쪽 식에 count++, 상태 변경, 예외 발생 가능성이 있으면 실행 여부 차이가 결과 외의 관측값을 바꿉니다. 반대로 오른쪽이 부수 효과 없는 단순 boolean 값이라면 이 비교에서 호출 횟수 차이는 없을 수 있습니다. 구체적인 코드에 메서드 호출이 있는지 보고 결론을 적용하세요.
정답을 가리고 확인해 보세요
이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.
false & check()와 false && check()를 실행하면 각각 결과와 check 호출 횟수는 어떻게 되나요?
정답과 해설 확인
두 식의 boolean 결과는 false입니다.&는 check를 호출하므로 calls가 1이고, &&는 오른쪽을 평가하지 않아 calls가 0입니다.JLS 21 §15.22.2의 boolean &는 논리 결과를 정하고, §15.23의 &&는 왼쪽 false에서 오른쪽 평가를 생략합니다. 두 절은 같은 규격 문서 안에 있습니다.