Java Conditionals — Interview Questions¶
Junior Level (5-7 Questions)¶
Q1: What are the conditional statements available in Java?¶
Answer
Java provides the following conditional constructs: 1. **`if`** — executes a block if a condition is `true` 2. **`if-else`** — executes one block if `true`, another if `false` 3. **`if-else if-else`** — chains multiple conditions 4. **`switch`** — selects one of many code blocks based on a value 5. **`ternary operator (?:)`** — inline conditional expressionpublic class Conditionals {
public static void main(String[] args) {
int score = 85;
// if-else if-else
if (score >= 90) {
System.out.println("Grade: A");
} else if (score >= 80) {
System.out.println("Grade: B");
} else {
System.out.println("Grade: C");
}
// switch
String day = "MON";
switch (day) {
case "MON": System.out.println("Monday"); break;
case "TUE": System.out.println("Tuesday"); break;
default: System.out.println("Other day");
}
// ternary
String status = (score >= 60) ? "Pass" : "Fail";
System.out.println("Status: " + status);
}
}
Q2: What is the difference between == and .equals() when used in conditions?¶
Answer
- `==` compares **references** (memory addresses) for objects, and **values** for primitives. - `.equals()` compares the **content/value** of objects.public class EqualityDemo {
public static void main(String[] args) {
// Primitives — use ==
int a = 5, b = 5;
System.out.println(a == b); // true
// Strings — use .equals()
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false (different objects)
System.out.println(s1.equals(s2)); // true (same content)
// String pool — tricky case
String s3 = "hello";
String s4 = "hello";
System.out.println(s3 == s4); // true (same pool reference)
}
}
Q3: What happens if you omit the break statement in a switch case?¶
Answer
Without `break`, execution **falls through** to the next case, executing all subsequent case blocks until a `break` is encountered or the switch ends.public class FallThroughDemo {
public static void main(String[] args) {
int day = 2;
switch (day) {
case 1: System.out.println("Monday");
case 2: System.out.println("Tuesday"); // matches here
case 3: System.out.println("Wednesday"); // falls through
case 4: System.out.println("Thursday"); // falls through
default: System.out.println("Other"); // falls through
}
// Output:
// Tuesday
// Wednesday
// Thursday
// Other
}
}
Q4: Can you use a switch statement with String values? What types does switch support?¶
Answer
Yes, `switch` supports `String` since **Java 7**.public class SwitchTypesDemo {
enum Season { SPRING, SUMMER, FALL, WINTER }
public static void main(String[] args) {
// Switch on String (Java 7+)
String command = "start";
switch (command) {
case "start": System.out.println("Starting..."); break;
case "stop": System.out.println("Stopping..."); break;
case "restart": System.out.println("Restarting..."); break;
default: System.out.println("Unknown command");
}
// Switch on enum (Java 5+)
Season s = Season.SUMMER;
switch (s) {
case SUMMER: System.out.println("Hot"); break;
case WINTER: System.out.println("Cold"); break;
default: System.out.println("Mild");
}
}
}
Q5: What is the ternary operator? When should you use it vs if-else?¶
Answer
The ternary operator is a shorthand conditional expression: `condition ? valueIfTrue : valueIfFalse`.public class TernaryDemo {
public static void main(String[] args) {
int age = 20;
// Ternary — good for simple assignments
String status = (age >= 18) ? "Adult" : "Minor";
System.out.println(status); // Adult
int a = 10, b = 20;
int max = (a > b) ? a : b;
System.out.println("Max: " + max); // 20
// Bad — nested ternary is hard to read
int score = 85;
// String grade = (score >= 90) ? "A" : (score >= 80) ? "B" : (score >= 70) ? "C" : "F";
// Better — use if-else for multiple conditions
String grade;
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
else if (score >= 70) grade = "C";
else grade = "F";
System.out.println("Grade: " + grade);
}
}
Q6: What data types can be used as a condition in an if statement?¶
Answer
Only `boolean` and `Boolean` (auto-unboxed) can be used as conditions in Java.public class ConditionTypeDemo {
public static void main(String[] args) {
// Valid
boolean flag = true;
if (flag) System.out.println("flag is true");
if (10 > 5) System.out.println("comparison returns boolean");
Boolean wrapped = Boolean.TRUE;
if (wrapped) System.out.println("auto-unboxed to boolean");
// Invalid — unlike C/C++, Java does NOT treat integers as booleans
// int x = 1;
// if (x) { } // COMPILATION ERROR: incompatible types
// Invalid — strings are not booleans
// String s = "true";
// if (s) { } // COMPILATION ERROR
// Pitfall with Boolean wrapper
Boolean nullFlag = null;
// if (nullFlag) { } // NullPointerException at runtime!
}
}
Q7: What is short-circuit evaluation in Java conditionals?¶
Answer
Short-circuit evaluation means Java stops evaluating a logical expression as soon as the result is determined: - `&&` (AND): If the left side is `false`, the right side is **not evaluated** (result is already `false`). - `||` (OR): If the left side is `true`, the right side is **not evaluated** (result is already `true`).public class ShortCircuitDemo {
public static void main(String[] args) {
// Short-circuit prevents NullPointerException
String name = null;
if (name != null && name.length() > 5) {
System.out.println("Long name");
} else {
System.out.println("Name is null or short");
}
// Without short-circuit (&), it would crash
// if (name != null & name.length() > 5) { }
// NullPointerException! Both sides always evaluated
// Short-circuit with ||
boolean cached = true;
if (cached || expensiveDatabaseCall()) {
System.out.println("Using cached value");
// expensiveDatabaseCall() is never called
}
}
static boolean expensiveDatabaseCall() {
System.out.println("Database called!");
return false;
}
}
Middle Level (4-6 Questions)¶
Q1: What are switch expressions (Java 14+) and how do they differ from traditional switch statements?¶
Answer
Switch expressions (preview in Java 12-13, standard in Java 14) return a value, use arrow syntax (`->`), have no fall-through, and must be exhaustive.public class SwitchExpressionDemo {
public static void main(String[] args) {
int day = 3;
// Traditional switch statement
String name1;
switch (day) {
case 1: name1 = "Monday"; break;
case 2: name1 = "Tuesday"; break;
case 3: name1 = "Wednesday"; break;
default: name1 = "Other"; break;
}
// Switch expression (Java 14+)
String name2 = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
default -> "Other";
};
System.out.println(name1 + " = " + name2); // Wednesday = Wednesday
// Multiple labels and yield for blocks
String dayType = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> {
System.out.println("Invalid day");
yield "Unknown"; // yield returns value from block
}
};
System.out.println("Day type: " + dayType);
}
}
Q2: What is pattern matching for instanceof (Java 16+)? How does it improve conditional logic?¶
Answer
Pattern matching eliminates redundant casting after `instanceof` checks:public class PatternMatchingDemo {
public static void main(String[] args) {
Object obj = "Hello, World!";
// Before Java 16 — verbose and repetitive
if (obj instanceof String) {
String s = (String) obj; // explicit cast required
System.out.println("Length: " + s.length());
}
// Java 16+ — pattern variable (s is already cast)
if (obj instanceof String s) {
System.out.println("Length: " + s.length());
}
// Works with logical operators
if (obj instanceof String s && s.length() > 5) {
System.out.println("Long string: " + s.toUpperCase());
}
// Negation pattern — flow scoping
if (!(obj instanceof String s)) {
System.out.println("Not a string");
return;
}
// s IS in scope here due to flow scoping
System.out.println("It is a string: " + s);
}
// Switch with pattern matching (Java 21)
static String describe(Object obj) {
return switch (obj) {
case Integer i when i < 0 -> "Negative: " + i;
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
case null -> "null";
default -> "Unknown: " + obj.getClass().getSimpleName();
};
}
}
Q3: How do you handle null values safely in conditional logic? What patterns do you use?¶
Answer
Multiple strategies for null-safe conditionals:import java.util.Objects;
import java.util.Optional;
public class NullSafetyDemo {
record Address(String city) {}
record User(String name, Address address) {}
public static void main(String[] args) {
User user = new User("Alice", null);
// 1. Null check first (traditional)
if (user != null
&& user.address() != null
&& user.address().city() != null) {
System.out.println(user.address().city().toUpperCase());
}
// 2. Optional chain (Java 8+)
String city = Optional.ofNullable(user)
.map(User::address)
.map(Address::city)
.map(String::toUpperCase)
.orElse("Unknown");
System.out.println("City: " + city);
// 3. Objects.requireNonNull — fail-fast validation
try {
Objects.requireNonNull(user.address(), "address must not be null");
} catch (NullPointerException e) {
System.out.println("Caught: " + e.getMessage());
}
// 4. Yoda conditions — avoids NPE if variable is null
String status = null;
if ("ACTIVE".equals(status)) {
System.out.println("Active");
} else {
System.out.println("Not active (status is null)");
}
// 5. Switch with null (Java 21+)
// String result = switch (status) {
// case null -> "null!";
// case "ACTIVE" -> "Active";
// default -> "other";
// };
}
}
Q4: Explain the difference between if-else if chains and switch in terms of performance and readability.¶
Answer
**Performance comparison:**public class IfVsSwitchDemo {
// if-else — sequential evaluation: O(n) worst case
static String classifyIfElse(int code) {
if (code == 200) return "OK";
else if (code == 301) return "Moved";
else if (code == 404) return "Not Found";
else if (code == 500) return "Server Error";
else return "Unknown";
}
// switch — compiled to tableswitch or lookupswitch bytecode
static String classifySwitch(int code) {
return switch (code) {
case 200 -> "OK";
case 301 -> "Moved";
case 404 -> "Not Found";
case 500 -> "Server Error";
default -> "Unknown";
};
}
public static void main(String[] args) {
System.out.println(classifyIfElse(404)); // Not Found
System.out.println(classifySwitch(404)); // Not Found
}
}
Q5: What are guard patterns in switch (Java 21+)?¶
Answer
Guard patterns add a `when` clause to switch cases, allowing additional boolean conditions after a type pattern:public class GuardPatternDemo {
static String classify(Object obj) {
return switch (obj) {
case Integer i when i < 0 -> "Negative integer: " + i;
case Integer i when i == 0 -> "Zero";
case Integer i -> "Positive integer: " + i;
case String s when s.isEmpty() -> "Empty string";
case String s when s.length() > 10 -> "Long string: " + s.substring(0, 10) + "...";
case String s -> "String: " + s;
case null -> "null";
default -> "Other: " + obj.getClass().getSimpleName();
};
}
public static void main(String[] args) {
System.out.println(classify(-5)); // Negative integer: -5
System.out.println(classify(0)); // Zero
System.out.println(classify(42)); // Positive integer: 42
System.out.println(classify("")); // Empty string
System.out.println(classify("Hello World!!!")); // Long string: Hello Worl...
System.out.println(classify("Hi")); // String: Hi
System.out.println(classify(null)); // null
System.out.println(classify(3.14)); // Other: Double
}
}
Q6: How does the JVM optimize conditional branches? What is branch prediction?¶
Answer
The JVM and CPU work together to optimize conditional branches: **1. JIT Profile-Guided Optimization:**public class BranchOptimizationDemo {
// If 99% of calls pass positive values, the JIT:
// - Places "positive" as the fall-through path (no jump needed)
// - Replaces "negative" with an uncommon trap
static String classify(int x) {
if (x > 0) return "positive"; // hot path — optimized
return "negative or zero"; // cold path — uncommon trap
}
public static void main(String[] args) {
// Warm up — JIT profiles the hot path
for (int i = 1; i <= 100_000; i++) {
classify(i);
}
System.out.println(classify(42)); // fast path
System.out.println(classify(-1)); // triggers deoptimization
}
}
int[] data = new int[100_000];
// Fill with random values 0-255
int sum = 0;
for (int val : data) {
if (val >= 128) { // branch prediction works well on sorted data
sum += val;
}
}
// Sorted: prediction accuracy ~100% (consistent pattern)
// Unsorted: prediction accuracy ~50% (random → many mispredictions)
// Sorted version can be 2-6x faster due to fewer pipeline stalls
Senior Level (4-6 Questions)¶
Q1: How does the JVM compile switch statements to bytecode? Explain tableswitch vs lookupswitch.¶
Answer
The Java compiler chooses between two bytecode instructions based on the density of case values: **`tableswitch` — dense cases (O(1) lookup):**// Consecutive values compile to tableswitch
static String denseSwitch(int x) {
return switch (x) {
case 0 -> "zero";
case 1 -> "one";
case 2 -> "two";
case 3 -> "three";
default -> "other";
};
}
// Bytecode: tableswitch 0 to 3
// 0: goto Label0 (direct indexed jump table)
// 1: goto Label1
// 2: goto Label2
// 3: goto Label3
// default: goto DefaultLabel
// Non-consecutive values compile to lookupswitch
static String sparseSwitch(int x) {
return switch (x) {
case 1 -> "a";
case 100 -> "b";
case 999 -> "c";
default -> "other";
};
}
// Bytecode: lookupswitch 3
// 1: goto Label0 (sorted key-offset pairs)
// 100: goto Label1
// 999: goto Label2
// default: goto DefaultLabel
Q2: How do sealed classes enable exhaustive switch expressions? What are the implications for API design?¶
Answer
Sealed classes (Java 17) restrict which classes can extend a type, enabling the compiler to verify exhaustiveness at compile time:public class SealedSwitchDemo {
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double a, double b, double c) implements Shape {}
// No default needed — compiler knows all subtypes
static double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> {
double s = (t.a() + t.b() + t.c()) / 2;
yield Math.sqrt(s * (s - t.a()) * (s - t.b()) * (s - t.c()));
}
};
}
public static void main(String[] args) {
System.out.printf("Circle area: %.2f%n", area(new Circle(5)));
System.out.printf("Rect area: %.2f%n", area(new Rectangle(4, 6)));
System.out.printf("Triangle area: %.2f%n", area(new Triangle(3, 4, 5)));
}
}
Q3: How would you design a rule engine using modern Java conditionals? Compare approaches for extensibility and performance.¶
Answer
A rule engine evaluates conditions and executes actions. Three approaches with different trade-offs: **1. If-else chain (simple, not extensible):**// Adding new rules requires modifying this method — violates Open/Closed Principle
double calculateDiscount(Order order) {
if (order.getTotal() > 1000 && order.isVip()) return 0.20;
else if (order.getTotal() > 500) return 0.10;
else if (order.getItems().size() > 10) return 0.05;
else return 0.0;
}
import java.util.*;
import java.util.function.Function;
public class RuleEngineDemo {
record Order(double total, int itemCount, boolean vip) {}
@FunctionalInterface
interface DiscountRule {
Optional<Double> evaluate(Order order);
}
public static void main(String[] args) {
List<DiscountRule> rules = List.of(
order -> order.total() > 1000 && order.vip()
? Optional.of(0.20) : Optional.empty(),
order -> order.total() > 500
? Optional.of(0.10) : Optional.empty(),
order -> order.itemCount() > 10
? Optional.of(0.05) : Optional.empty()
);
Order order = new Order(750.0, 5, false);
double discount = rules.stream()
.map(rule -> rule.evaluate(order))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElse(0.0);
System.out.printf("Discount: %.0f%%%n", discount * 100); // 10%
}
}
sealed interface Rule permits ThresholdRule, VipRule, BulkRule {}
record ThresholdRule(double minTotal, double discount) implements Rule {}
record VipRule(double discount) implements Rule {}
record BulkRule(int minItems, double discount) implements Rule {}
double applyRule(Rule rule, Order order) {
return switch (rule) {
case ThresholdRule t when order.getTotal() >= t.minTotal() -> t.discount();
case VipRule v when order.isVip() -> v.discount();
case BulkRule b when order.getItems().size() >= b.minItems() -> b.discount();
default -> 0.0;
};
}
Q4: What are the performance implications of autoboxing and unboxing in conditional expressions?¶
Answer
Autoboxing in conditionals causes subtle bugs and performance issues: **1. NullPointerException from unboxing:**public class AutoboxingPitfalls {
public static void main(String[] args) {
// NPE from Boolean unboxing
Boolean flag = null;
// if (flag) { } // NullPointerException! Unboxing null crashes
// NPE from Integer unboxing
Integer count = null;
// if (count > 0) { } // NullPointerException!
// 2. Ternary operator type coercion
Integer a = null;
int b = 5;
// int result = true ? a : b; // NPE! Ternary forces unboxing of a
// Both sides unbox to int because one operand is primitive int
// 3. Unexpected identity behavior with Integer cache
Integer x = 127, y = 127;
System.out.println(x == y); // true (Integer cache: -128 to 127)
Integer p = 128, q = 128;
System.out.println(p == q); // false! Different objects beyond cache range
// Always use .equals() for wrapper comparison
System.out.println(p.equals(q)); // true
// 4. Performance in tight loops
long start = System.nanoTime();
long sum = 0L; // primitive — fast
for (int i = 0; i < 1_000_000; i++) {
if (i % 2 == 0) {
sum += i; // no boxing overhead
}
}
long elapsed = System.nanoTime() - start;
System.out.println("Primitive sum: " + sum + " in " + elapsed / 1_000_000 + "ms");
// Using Long wrapper instead would be ~5x slower due to boxing on every += operation
}
}
Q5: What is the difference between megamorphic dispatch and pattern matching switch for conditional logic? When does each perform better?¶
Answer
HotSpot C2 JIT optimizes call sites based on the number of observed types: - **Monomorphic (1 type):** Inlined directly — fastest - **Bimorphic (2 types):** Inline cache with type check — fast - **Megamorphic (3+ types):** Virtual method table (vtable) lookup — slowerpublic class DispatchDemo {
interface Handler { String handle(); }
static class TypeA implements Handler { public String handle() { return "A"; } }
static class TypeB implements Handler { public String handle() { return "B"; } }
static class TypeC implements Handler { public String handle() { return "C"; } }
static class TypeD implements Handler { public String handle() { return "D"; } }
// Polymorphic dispatch — becomes megamorphic with 3+ types
static String polymorphicDispatch(Handler h) {
return h.handle(); // vtable lookup when megamorphic
}
// Pattern matching — JIT can optimize sealed type switches
sealed interface TypedHandler permits HandlerA, HandlerB, HandlerC, HandlerD {}
record HandlerA() implements TypedHandler {}
record HandlerB() implements TypedHandler {}
record HandlerC() implements TypedHandler {}
record HandlerD() implements TypedHandler {}
static String patternSwitch(TypedHandler h) {
return switch (h) {
case HandlerA a -> "A";
case HandlerB b -> "B";
case HandlerC c -> "C";
case HandlerD d -> "D";
};
}
public static void main(String[] args) {
// Megamorphic dispatch
Handler[] handlers = { new TypeA(), new TypeB(), new TypeC(), new TypeD() };
for (Handler h : handlers) {
System.out.println("Poly: " + polymorphicDispatch(h));
}
// Pattern matching dispatch
TypedHandler[] typed = { new HandlerA(), new HandlerB(), new HandlerC(), new HandlerD() };
for (TypedHandler h : typed) {
System.out.println("Pattern: " + patternSwitch(h));
}
}
}
Scenario-Based Questions (3-5)¶
Scenario 1: A junior developer wrote a method that classifies HTTP status codes using a 40+ line if-else chain. The code is correct but unmaintainable. How would you refactor it?¶
Answer
Step-by-step refactoring approach: **1. Identify the pattern — range-based classification:**// Before: 40+ conditions
if (code == 200) return "OK";
else if (code == 201) return "Created";
else if (code == 204) return "No Content";
// ... 37 more conditions
String classifyRange(int code) {
return switch (code / 100) {
case 1 -> "Informational";
case 2 -> "Success";
case 3 -> "Redirection";
case 4 -> "Client Error";
case 5 -> "Server Error";
default -> "Unknown";
};
}
import java.util.*;
import java.util.stream.Collectors;
import java.util.function.Function;
public class HttpStatusRefactored {
enum HttpStatus {
OK(200, "OK"),
CREATED(201, "Created"),
NO_CONTENT(204, "No Content"),
BAD_REQUEST(400, "Bad Request"),
NOT_FOUND(404, "Not Found"),
INTERNAL_ERROR(500, "Internal Server Error");
final int code;
final String description;
private static final Map<Integer, HttpStatus> BY_CODE =
Arrays.stream(values())
.collect(Collectors.toMap(s -> s.code, Function.identity()));
HttpStatus(int code, String description) {
this.code = code;
this.description = description;
}
static HttpStatus fromCode(int code) {
return BY_CODE.get(code);
}
}
public static void main(String[] args) {
HttpStatus status = HttpStatus.fromCode(404);
System.out.println(status != null ? status.description : "Unknown"); // Not Found
}
}
Scenario 2: In production, you notice a NullPointerException occurring intermittently in a conditional block that has been working for months. The code looks correct. How do you diagnose and fix it?¶
Answer
**Systematic diagnosis approach:** **1. Reproduce and identify the exact line:**// Suspect code — method chain where any link can return null
public String processOrder(Order order) {
if (order.getCustomer().getAddress().getCity().equals("NYC")) {
return applyNYCTax(order);
}
return applyDefaultTax(order);
}
import java.util.Optional;
public class NullDiagnosisDemo {
record Address(String city) {}
record Customer(Address address) {}
record Order(Customer customer, double total) {}
// Option A: Guard clauses with early return
static String processOrderSafe(Order order) {
if (order == null || order.customer() == null
|| order.customer().address() == null
|| order.customer().address().city() == null) {
return "default tax";
}
if ("NYC".equals(order.customer().address().city())) {
return "NYC tax applied";
}
return "default tax";
}
// Option B: Optional chain (cleaner)
static String processOrderOptional(Order order) {
boolean isNYC = Optional.ofNullable(order)
.map(Order::customer)
.map(Customer::address)
.map(Address::city)
.map("NYC"::equals)
.orElse(false);
return isNYC ? "NYC tax applied" : "default tax";
}
public static void main(String[] args) {
// The intermittent case — customer has no address
Order problematicOrder = new Order(new Customer(null), 100.0);
System.out.println(processOrderSafe(problematicOrder)); // default tax
System.out.println(processOrderOptional(problematicOrder)); // default tax
}
}
Scenario 3: Your team is debating whether to use polymorphism or switch/instanceof for handling 20+ different message types in a message processing system. What do you recommend?¶
Answer
**Analysis of both approaches:** **Approach A: Polymorphism (OOP)**interface Message {
void process(MessageContext ctx);
}
class EmailMessage implements Message {
public void process(MessageContext ctx) { /* email logic */ }
}
class SmsMessage implements Message {
public void process(MessageContext ctx) { /* SMS logic */ }
}
// 20+ classes, each with process()
import java.util.*;
public class MessageRoutingDemo {
sealed interface Message permits EmailMsg, SmsMsg, PushMsg {}
record EmailMsg(String to, String body) implements Message {}
record SmsMsg(String phone, String text) implements Message {}
record PushMsg(String deviceId, String title) implements Message {}
static void process(Message msg) {
switch (msg) {
case EmailMsg e -> System.out.println("Sending email to: " + e.to());
case SmsMsg s -> System.out.println("Sending SMS to: " + s.phone());
case PushMsg p -> System.out.println("Pushing to device: " + p.deviceId());
// Compiler enforces all types are handled
}
}
public static void main(String[] args) {
List<Message> messages = List.of(
new EmailMsg("alice@example.com", "Hello"),
new SmsMsg("+1234567890", "Hi"),
new PushMsg("device-abc", "Notification")
);
messages.forEach(MessageRoutingDemo::process);
}
}
Scenario 4: A performance-sensitive trading application has a method with nested conditionals called millions of times per second. Profiling shows it is a hotspot. How do you optimize it?¶
Answer
**Systematic optimization approach:** **1. Original code (nested conditionals):**double calculateFee(Trade trade) {
if (trade.getType() == TradeType.EQUITY) {
if (trade.getVolume() > 10_000) {
if (trade.isInstitutional()) return trade.getValue() * 0.001;
else return trade.getValue() * 0.002;
} else {
return trade.getValue() * 0.005;
}
} else if (trade.getType() == TradeType.OPTION) {
if (trade.getVolume() > 100) return trade.getContracts() * 0.50;
else return trade.getContracts() * 0.75;
} else if (trade.getType() == TradeType.FUTURES) {
return trade.getContracts() * 1.25;
}
return 0.0;
}
public class TradingOptimization {
enum TradeType { EQUITY, OPTION, FUTURES }
// Encode all conditions into an array index
// [type][highVolume][institutional] = fee rate
static final double[][][] FEE_TABLE = new double[3][2][2];
static {
FEE_TABLE[0][1][1] = 0.001; // EQUITY, high volume, institutional
FEE_TABLE[0][1][0] = 0.002; // EQUITY, high volume, retail
FEE_TABLE[0][0][1] = 0.005; // EQUITY, low volume, institutional
FEE_TABLE[0][0][0] = 0.005; // EQUITY, low volume, retail
FEE_TABLE[1][1][0] = 0.50; // OPTION, high volume
FEE_TABLE[1][0][0] = 0.75; // OPTION, low volume
FEE_TABLE[2][0][0] = 1.25; // FUTURES
FEE_TABLE[2][1][0] = 1.25; // FUTURES
}
static double calculateFeeOptimized(int typeOrdinal, boolean highVolume, boolean institutional, double value) {
double rate = FEE_TABLE[typeOrdinal][highVolume ? 1 : 0][institutional ? 1 : 0];
return value * rate; // single multiplication, no branches
}
public static void main(String[] args) {
double fee = calculateFeeOptimized(
TradeType.EQUITY.ordinal(), true, true, 100_000.0
);
System.out.printf("Fee: $%.2f%n", fee); // $100.00
}
}
// Pre-sort trades by type, then process in monomorphic loops
Map<TradeType, List<Trade>> grouped = trades.stream()
.collect(Collectors.groupingBy(Trade::getType));
// Each loop is branch-predictor-friendly (same path every time)
for (Trade t : grouped.get(TradeType.EQUITY)) { /* ... */ }
for (Trade t : grouped.get(TradeType.OPTION)) { /* ... */ }
FAQ¶
Q: What types of questions about conditionals are most commonly asked in Java interviews?¶
A: At the junior level, interviewers focus on syntax (if-else, switch, ternary), common mistakes (missing break, == vs .equals()), and understanding of boolean logic. At the middle level, expect questions about switch expressions (Java 14+), pattern matching (Java 16+), null handling, and when to use switch vs if-else. Senior interviews focus on JVM bytecode (tableswitch vs lookupswitch), performance optimization, design patterns for complex conditional logic, and sealed classes.
Q: How much should I know about modern Java features like switch expressions and pattern matching?¶
A: It depends on the company and the Java version they use. For companies using Java 17+ (most modern projects), you should be comfortable with switch expressions, pattern matching for instanceof, and sealed classes. For legacy codebases (Java 8-11), focus on traditional switch, Optional for null safety, and polymorphism as a replacement for complex conditionals. Always mention you know about newer features, even if the codebase does not use them yet.
Q: Should I write switch expressions or traditional switch statements in coding interviews?¶
A: If the interviewer does not specify a Java version, ask which version the team uses. If Java 14+, prefer switch expressions — they demonstrate modern knowledge, are more concise, and the compiler enforces exhaustiveness. If unsure, write the traditional form first and then mention: "With Java 14+, I would use a switch expression here for better safety and readability."
Q: What makes a great answer to conditional-related interview questions?¶
A: Key evaluation criteria: - Junior: Correct syntax, awareness of common pitfalls (== vs .equals(), fall-through), ability to write working code - Middle: Understanding of when to use which conditional construct, null safety patterns, familiarity with modern Java features (switch expressions, pattern matching) - Senior: Knowledge of JVM internals (bytecode, JIT optimization), ability to design extensible conditional logic (strategy pattern, sealed classes), performance awareness, and ability to articulate trade-offs between different approaches
Q: How important is it to know about branch prediction and JIT optimizations?¶
A: For junior and middle positions, it is rarely asked. For senior and staff-level positions, understanding how the JVM optimizes conditionals is a strong differentiator. You do not need to memorize CPU pipeline details, but knowing that (1) the JIT profiles branches and optimizes hot paths, (2) sorted data improves branch prediction, and (3) lookup tables can replace branches in hot paths demonstrates deep understanding of systems-level thinking.