본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
3장 : 연산자와 표현식

비교와 논리 연산

조건문에 들어가는 표현식은 결국 하나의 boolean 값을 만듭니다.

숫자 비교와 객체 비교는 같은 == 기호를 사용해도 의미가 다릅니다.

논리 연산자는 여러 조건을 조합하며 단락 평가를 통해 뒤 조건을 실행하지 않을 수 있습니다.

이 특성은 성능 최적화보다 안전한 접근 순서를 만드는 데 더 중요합니다.

여기서는 조건을 읽는 순서와 비교 대상의 정체를 함께 확인합니다.


값 비교와 참조 비교를 나눈다

기본 타입의 ==는 값을 비교하지만 객체 참조의 ==는 같은 객체인지 비교합니다.

문자열 내용은 equals를 사용해야 합니다.


&&||는 필요한 만큼만 평가한다

&&는 앞 조건이 거짓이면 뒤를 평가하지 않고 ||는 앞 조건이 참이면 멈춥니다.

null 검사 뒤에 메서드 호출을 두는 순서가 안전한 이유입니다.

LogicalChecks.java는 null 검사 뒤의 메서드 호출과 같은 글자를 가진 두 문자열의 비교를 한 번에 보여 줍니다. 어떤 식은 호출조차 되지 않고 어떤 비교는 거짓이 되는지 이유를 나누어 봅니다.

LogicalChecks.java
public class LogicalChecks {
    public static void main(String[] args) {
        String input = null;
        boolean usable = input != null && !input.isBlank();
        System.out.println("usable=" + usable);

        String left = new String("java");
        String right = "java";
        System.out.println("same object=" + (left == right));
        System.out.println("same text=" + left.equals(right));

        // boolean broken = input != null & !input.isBlank(); // 실행 시 NPE: 단락 평가 안 함
    }
}

오른쪽 조건이 호출되지 않는 출력은 단락 평가가 단순 최적화가 아니라 실행 의미라는 증거입니다. value != null && !value.isBlank()처럼 안전 조건을 먼저 두면 두 번째 식은 첫 번째가 참일 때만 평가됩니다.

usable=false
same object=false
same text=true

&&는 첫 조건이 거짓이면 뒤 메서드를 호출하지 않아 null 접근을 막습니다.

왼쪽 조건 평가

&&|| 모두 왼쪽 피연산자부터 계산합니다. 이 식이 전체 결과를 확정할 수 있는지가 다음 호출의 실행 여부를 결정합니다.

결과로 뒤 조건 실행 여부 결정

&&는 왼쪽이 거짓이면 멈추고 ||는 왼쪽이 참이면 멈춥니다. null 검사나 비용이 큰 호출은 이 순서를 의도적으로 사용합니다.

최종 boolean으로 분기

필요한 피연산자만 평가한 결과가 하나의 boolean이 되어 if에 전달됩니다. 오른쪽 식의 부작용에 기대면 조건 순서를 바꾸기 어려워지므로 피합니다.


부정은 조건 전체에 적용한다

복합 조건을 부정할 때 드모르간 법칙으로 연산자가 함께 바뀝니다.

긴 부정식은 긍정적인 이름의 불리언 변수로 분리하면 읽기 쉽습니다.

!(age >= 18 && hasTicket)age < 18 || !hasTicket과 같습니다. 부정을 바깥에 둔 식과 드모르간 법칙으로 풀어 쓴 식에 경계값 17, 18, 19를 넣어 보면 연산자뿐 아니라 비교 방향도 함께 바뀌어야 함을 확인할 수 있습니다.

비교 1
int age = 18;
boolean adult = age >= 18;
boolean outside = age < 0 || age > 130;
boolean inside = !outside;
비교 2
double value = Double.NaN;
System.out.println(value == value);       // false
System.out.println(Double.isNaN(value));  // true

조건의 경계값을 직접 써 본다

미만과 이하, 초과와 이상의 차이는 경계 하나에서만 드러납니다.

최소값, 바로 아래, 최대값, 바로 위를 대입해 조건을 확인하면 실수를 줄일 수 있습니다.


부동소수점 동등 비교에는 허용 오차가 필요하다

계산 결과 double은 표현 오차가 있으므로 a == b 대신 문제 영역에 맞는 허용 오차를 비교합니다.

NaN은 자기 자신과도 같지 않다는 규칙도 확인합니다.

허용 오차는 무조건 0.000001로 고정하는 값이 아니라 데이터 규모와 요구 정밀도에서 정해야 합니다. 매우 큰 수나 매우 작은 수를 비교할 때는 절대 오차와 상대 오차 가운데 어떤 기준이 문제 계약에 맞는지 먼저 결정합니다.


좋은 조건식은 비교 대상, 단락 평가 순서, 경계값을 코드만 보고도 설명할 수 있습니다.