증감·비교·논리·대입
상태를 바꾸는 연산과 값을 만드는 연산을 분리하고 단축 평가를 이용해 안전한 회원가입 조건을 구성합니다.
count++는 값을 읽는 동시에 변수를 바꾸고, age >= 30은 변수를 바꾸지 않은 채 boolean 값을 만듭니다.
=는 대입이고 ==는 비교입니다.
기호가 짧다고 한 식에 섞으면 “어떤 값이 계산됐는가”와 “어떤 상태가 바뀌었는가”를 동시에 추적해야 합니다.
후위 증가 연산
public final class PostIncrementTrap {
public static void main(String[] args) {
int memberCount = 0;
int printedCount = memberCount++;
System.out.println("printed=" + printedCount);
System.out.println("stored=" + memberCount);
}
}printed=0
stored=1후위 memberCount++는 식의 결과로 변경 전 값 0을 내놓고, 그 뒤 변수에 1을 저장합니다.
전위 ++memberCount는 먼저 1을 저장한 뒤 식의 결과도 1로 만듭니다.
단독 문장으로 사용할 때는 둘 다 1을 증가시킵니다.
memberCount++;
System.out.println(memberCount);카운터를 증가시키면서 동시에 다른 변수에 대입하거나 메서드 인자로 넘기면 전위·후위 차이를 매번 해석해야 합니다.
회원가입에서는 상태 변경을 한 문장으로 분리해 증가 뒤의 값을 명확히 사용합니다.
--도 같은 규칙으로 1을 감소시킵니다.
회원 나이처럼 0 아래로 내려가면 안 되는 값은 연산자 자체가 막지 않으므로 별도 조건이 필요합니다.
대입과 비교
다음 코드는 active가 참인지 확인하는 대신 참을 대입합니다.
대입식의 결과도 대입한 값 true이므로 출력만 보면 비교가 성공한 것처럼 보입니다.
public final class AssignmentInsteadOfComparison {
public static void main(String[] args) {
boolean active = false;
System.out.println("expression=" + (active = true));
System.out.println("stored=" + active);
}
}expression=true
stored=true같은지 비교하는 연산자는 ==입니다.
boolean은 if (active)처럼 값 자체를 조건으로 읽을 수 있어 active == true보다 간단합니다.
문자열의 내용 비교에는 ==가 아니라 equals를 사용한다는 차이는 참조를 배운 뒤 자세히 다룹니다.
boolean 결과값
public final class MemberConditions {
public static void main(String[] args) {
int age = 45;
int minimumAge = 18;
boolean emailPresent = true;
boolean positive = age > 0;
boolean withinLimit = age <= 120;
boolean valid = emailPresent && positive && withinLimit;
boolean active = age >= minimumAge;
boolean needsAttention = !valid || !active;
System.out.println("positive=" + positive);
System.out.println("withinLimit=" + withinLimit);
System.out.println("valid=" + valid);
System.out.println("active=" + active);
System.out.println("needsAttention=" + needsAttention);
}
}positive=true
withinLimit=true
valid=true
active=true
needsAttention=false<, <=, >, >=, ==, !=는 두 값을 비교해 boolean을 만듭니다.
&&는 두 조건이 모두 참일 때, ||는 하나 이상 참일 때 참입니다.
!는 참과 거짓을 뒤집습니다.
valid처럼 복합 조건에 이름을 붙이면 출력과 분기에서 같은 규칙을 재사용할 수 있습니다.
age > 0 && age <= 120은 1부터 120까지의 닫힌 범위를 표현합니다.
경계값 1과 120을 포함하려면 두 번째 비교에 <=가 필요합니다.
0 < age <= 120처럼 수학식의 연쇄 비교는 Java 문법이 아닙니다.
비교 두 개를 논리 연산자로 연결합니다.
단축 평가
&&는 왼쪽이 거짓이면 전체 결과가 이미 거짓이므로 오른쪽을 평가하지 않습니다.
||는 왼쪽이 참이면 오른쪽을 평가하지 않습니다.
public final class ShortCircuitGuard {
public static void main(String[] args) {
int totalAge = 120;
int memberCount = 0;
boolean averageAtLeastThirty = memberCount != 0
&& (totalAge / memberCount) >= 30;
System.out.println("averageAtLeastThirty=" + averageAtLeastThirty);
}
}averageAtLeastThirty=false왼쪽 memberCount != 0이 거짓이므로 0으로 나누는 오른쪽 식은 실행되지 않습니다.
순서를 뒤집으면 예외가 먼저 납니다.
boolean broken = (totalAge / memberCount) >= 30
&& memberCount != 0;단축 평가를 단지 성능 최적화로 보지 않습니다.
오른쪽 식을 평가해도 안전한지 왼쪽 조건이 보장하는 가드로 사용합니다.
다만 오른쪽에서 변수 증가나 파일 쓰기 같은 부수 효과를 일으키면 조건에 따라 실행 여부가 달라지므로 논리식 안에는 상태 변경을 넣지 않는 편이 안전합니다.
복합 대입
public final class CompoundAssignment {
public static void main(String[] args) {
int totalAge = 40;
totalAge += 30;
totalAge -= 10;
totalAge *= 2;
totalAge /= 4;
System.out.println("total=" + totalAge);
}
}total=30totalAge += 30은 현재 값을 읽어 30을 더한 뒤 같은 변수에 저장한다는 뜻입니다.
대체로 totalAge = totalAge + 30과 같은 의도지만, 복합 대입에는 암시적 타입 변환 규칙도 있어 작은 숫자 타입에서는 완전히 같은 식으로 치환되지 않을 수 있습니다.
지금은 int 안에서만 사용하고 형변환 절에서 차이를 다시 확인합니다.
연산 우선순위상 산술 비교 논리 대입 순서로 묶이지만, 다음처럼 의미를 변수로 나누는 것이 읽기 쉽습니다.
boolean withinRange = age >= 1 && age <= 120;
boolean acceptable = emailPresent && withinRange;
active = acceptable && age >= minimumAge;입력 상태 계산
회원가입 입력에서 아직 분기를 하지 않고 판정값만 출력해 봅니다.
public final class MemberFlags {
public static void main(String[] args) {
String email = args[0];
int age = Integer.parseInt(args[1]);
int minimumAge = Integer.parseInt(args[2]);
boolean emailPresent = !email.isBlank();
boolean ageValid = age >= 1 && age <= 120;
boolean minimumAgeValid = minimumAge >= 1 && minimumAge <= 120;
boolean valid = emailPresent && ageValid && minimumAgeValid;
boolean active = valid && age >= minimumAge;
System.out.println("valid=" + valid);
System.out.println("active=" + active);
}
}java -cp out MemberFlags logical@example.com 45 60은 valid=true, active=false를 출력합니다.
java -cp out MemberFlags logical@example.com 700 60은 valid=false이고 단축 평가 때문에 활성으로 판정되지 않습니다.
다음 절에서는 이 두 boolean에 따라 저장·오류 출력 경로를 실제로 나눕니다.
연습 문제
이메일이 비어 있지 않고, 나이가 18세 이상 120세 이하이며, 로그인 실패 횟수가 2 이하일 때만 eligible=true가 되는 SignupFlags를 작성하세요.
age=50, failedLoginCount=3을 사용해 각 부분 조건과 최종 조건을 모두 출력합니다.
해설 보기
public final class SignupFlags {
public static void main(String[] args) {
String email = "logical@example.com";
int age = 50;
int failedLoginCount = 3;
boolean emailPresent = !email.isBlank();
boolean ageAllowed = age >= 18 && age <= 120;
boolean loginAttemptsAllowed = failedLoginCount <= 2;
boolean eligible = emailPresent
&& ageAllowed
&& loginAttemptsAllowed;
System.out.println("emailPresent=" + emailPresent);
System.out.println("ageAllowed=" + ageAllowed);
System.out.println("loginAttemptsAllowed=" + loginAttemptsAllowed);
System.out.println("eligible=" + eligible);
}
}emailPresent=true
ageAllowed=true
loginAttemptsAllowed=false
eligible=false최종 결과만 출력하면 어떤 조건이 실패했는지 알 수 없습니다.
연습 단계에서는 부분 조건에 이름을 붙여 함께 출력하고, 실제 CLI에서는 사용자에게 필요한 실패 이유를 분기해 안내합니다.