Java Conditions
A condition is a boolean expression that a program evaluates to decide what to do next. Every branch, loop, and filter in Java depends on one. This lesson covers how conditions are built and which Java constructs act on them; the following lessons cover if/else and switch in detail.
What Counts as a Condition
A condition must have type boolean. It can be:
true // a literal
isReady // a boolean variable
age >= 18 // a comparison
name.isEmpty() // a method returning boolean
obj instanceof String // a type test
a > 0 && b > 0 // a combination of the above
Java does not convert other types into booleans. Numbers, strings, and object references are never "truthy" or "falsy" as they are in JavaScript or Python:
| Intent | Not valid Java | Valid Java |
|---|---|---|
| Number is non-zero | if (count) | if (count != 0) |
| Reference is not null | if (user) | if (user != null) |
| String has content | if (text) | if (text != null && !text.isEmpty()) |
| List has items | if (items) | if (!items.isEmpty()) |
Writing the full comparison makes every condition say exactly what it checks.
Comparison Operators in Conditions
int a = 10, b = 20;
a == b // false: equal to
a != b // true: not equal to
a < b // true: less than
a > b // false: greater than
a <= 10 // true: less than or equal to
b >= 25 // false: greater than or equal to
For objects, == tests whether two references point to the same object. Content comparison requires equals:
String input = scanner.nextLine();
if (input == "quit") { ... } // wrong: compares references, usually false
if (input.equals("quit")) { ... } // correct: compares characters
if ("quit".equals(input)) { ... } // correct and null-safe
Enum constants are the exception: each constant is a single object, so status == Status.ACTIVE is correct and idiomatic.
For ordering objects that do not support < and >, use compareTo, which returns a negative number, zero, or a positive number:
if (name1.compareTo(name2) < 0) { ... } // name1 sorts before name2
if (date1.isBefore(date2)) { ... } // LocalDate provides readable methods
if (price.compareTo(BigDecimal.ZERO) > 0) { ... } // BigDecimal comparison
Combining Conditions
boolean inRange = score >= 0 && score <= 100;
boolean weekend = day.equals("SAT") || day.equals("SUN");
boolean notBanned = !user.isBanned();
Java has no chained comparison: 0 <= score <= 100 is a compile error, because 0 <= score produces a boolean, which cannot be compared with 100. Each comparison must be written separately and joined with &&.
Short-circuit order matters
&& stops at the first false; || stops at the first true. Place guard conditions first:
if (list != null && !list.isEmpty() && list.get(0).equals(target)) { ... }
Each check protects the one after it. Reversing the order would throw NullPointerException or IndexOutOfBoundsException before the guard ran.
Also place cheap checks before expensive ones when either order is safe:
if (isEnabled && expensiveDatabaseLookup(id)) { ... }
Precedence and parentheses
! binds tightest, then comparisons, then &&, then ||. So:
a || b && c // means a || (b && c)
!a && b // means (!a) && b
When a condition mixes && and ||, add parentheses even where precedence already gives the right answer. They make the grouping explicit to the reader:
if ((isAdmin || isOwner) && !isLocked) { ... }
Naming Complex Conditions
A long condition inside an if forces the reader to decode it. Assigning parts to named variables or extracting a method documents the intent:
// Hard to read
if (order.getTotal() > 500 && customer.getYears() >= 2 && !customer.hasUnpaidInvoices()) {
applyDiscount(order);
}
// Clear
boolean largeOrder = order.getTotal() > 500;
boolean loyalCustomer = customer.getYears() >= 2;
boolean goodStanding = !customer.hasUnpaidInvoices();
if (largeOrder && loyalCustomer && goodStanding) {
applyDiscount(order);
}
Or, when the rule is reused:
private boolean qualifiesForDiscount(Order order, Customer customer) {
return order.getTotal() > 500
&& customer.getYears() >= 2
&& !customer.hasUnpaidInvoices();
}
Constructs That Use Conditions
Java offers several ways to act on a condition. Each fits a different shape of decision.
| Construct | Use when | Example |
|---|---|---|
if | Run code only when a condition holds | if (x < 0) x = 0; |
if/else | Choose between two paths | Pass or fail |
if/else if/else | Choose among ranges or unrelated conditions | Grade bands |
switch | Choose by matching one value against constants or patterns | Menu option, enum, day name |
Ternary ?: | Choose between two values in an expression | String s = n == 1 ? "item" : "items"; |
| Loop conditions | Repeat while a condition holds | while (!done) |
A brief view of each, before their dedicated lessons:
int temp = 31;
// if / else if / else
if (temp > 35) {
System.out.println("Heat warning");
} else if (temp > 28) {
System.out.println("Hot");
} else {
System.out.println("Comfortable");
}
// ternary
String label = temp > 28 ? "hot" : "mild";
// switch expression
String day = "SAT";
String type = switch (day) {
case "SAT", "SUN" -> "weekend";
default -> "weekday";
};
The ternary operator is an expression: it produces a value and fits anywhere a value does, such as an assignment, a method argument, or a return. It is for choosing a value, not for running statements; use if when each branch does work.
System.out.println("Found " + count + (count == 1 ? " result" : " results"));
return balance >= amount ? balance - amount : balance;
Pattern Matching in Conditions
Since Java 16, instanceof can test a type and bind a variable in one step. The variable is in scope wherever the condition is known to be true:
Object value = getValue();
if (value instanceof String s && !s.isBlank()) {
System.out.println("Text: " + s.strip());
} else if (value instanceof Integer n && n > 0) {
System.out.println("Positive number: " + n);
}
s can be used on the right side of && because that side runs only when the instanceof test succeeded. It cannot be used after ||, where the test may have failed.
Floating-Point Conditions
Exact equality on double values is unreliable because of binary rounding:
double total = 0.1 + 0.2;
if (total == 0.3) { ... } // false
if (Math.abs(total - 0.3) < 1e-9) { ... } // true
Range comparisons (<, >=) are usually safe. For money, use BigDecimal and compareTo.
Practical Example: Shipping Rules
public class Shipping {
static double shippingCost(double orderTotal, double weightKg,
boolean isPrime, String country) {
boolean domestic = "IN".equals(country);
boolean freeEligible = domestic && (isPrime || orderTotal >= 499);
boolean heavy = weightKg > 10;
if (freeEligible && !heavy) {
return 0;
}
double base = domestic ? 40 : 1200;
double weightCharge = heavy ? (weightKg - 10) * 15 : 0;
return base + weightCharge;
}
public static void main(String[] args) {
System.out.println(shippingCost(650, 2, false, "IN")); // 0.0
System.out.println(shippingCost(300, 2, false, "IN")); // 40.0
System.out.println(shippingCost(300, 14, true, "IN")); // 100.0
System.out.println(shippingCost(900, 3, true, "US")); // 1200.0
}
}
Each business rule is a named boolean. "IN".equals(country) is null-safe. The parentheses in domestic && (isPrime || orderTotal >= 499) are required: without them the expression becomes (domestic && isPrime) || orderTotal >= 499, and every international order of 499 or more would ship free. The two ternaries choose values, while the if controls an early return. In the third call the order qualifies for free shipping but is heavy, so it pays the domestic base of 40 plus 4 kg × 15 = 60.