Representative interview topic

Java Coding Interview: How Do Primitive Type Patterns Change instanceof and switch?

CodingHard
Offer.cc Editorial TeamPublished Updated

Question

A Java 25 service wants to classify numeric values with pattern matching. Explain how primitive type patterns affect instanceof and switch, where narrowing is rejected, how null is handled, and how you would ship or avoid this preview feature.

Prompt and scope

Your team is evaluating Java 25's preview feature for primitive type patterns in instanceof, switch, and record patterns. The code receives Number values from a parser and must classify integral and floating-point cases without accidental narrowing or null failures. Explain the language rules, write a small example, and give a compatibility and rollout decision.

JEP 507 is a preview feature in Java 25. A good answer must say that preview syntax needs explicit compiler and runtime flags and is not automatically a stable production contract.

What the interviewer is testing

  • Whether you distinguish reference patterns from primitive patterns and boxing conversions.
  • Whether you can explain safe widening, unsafe narrowing, and loss of information.
  • Whether you handle null, NaN, infinity, and selector types explicitly.
  • Whether you understand exhaustive switch behavior and the preview compatibility boundary.
  • Whether you can design tests and a fallback rather than adopting syntax because it looks shorter.

The interviewer is looking for rules and counterexamples, not a list of JEP numbers.

Clarifications to ask first

  1. Is the build allowed to use preview features in CI and production? If not, the solution must use Java 21-era patterns or explicit conversions.
  2. Does “classify a number” preserve the original value, or is a bounded integer result acceptable? That changes whether narrowing is legal.
  3. Should null be rejected, mapped to UNKNOWN, or handled by a dedicated switch arm?
  4. Are NaN and infinities valid input? Numeric type matching does not turn them into ordinary finite values.
  5. Must the same source compile on a non-preview JDK? If yes, isolate the preview code behind a module or keep a stable implementation.

A 30-second answer framework

“Java 25's JEP 507 preview lets patterns mention primitive types in contexts that previously required boxing or awkward guards. The compiler permits conversions that are safe under the pattern rules and rejects narrowing that can lose information. I would handle null before primitive matching, test boundary values such as Integer.MAX_VALUE, NaN, and infinity, and keep the preview flag in the toolchain. If deployment cannot pin Java 25 with preview enabled, I would use a stable switch or explicit validated conversion instead.”

Step-by-step solution

1. Start with the value's static type

Pattern compatibility is not a license to convert every number to every primitive. A Number reference may hold an Integer, Long, Double, or another implementation. A pattern that first recognizes a reference wrapper and then unboxes preserves a different contract from a primitive type pattern over a primitive selector. State the selector type and conversion direction before discussing exhaustiveness.

java
static String classify(Number value) {
    if (value == null) {
        return "missing";
    }
    return switch (value) {
        case Integer i -> "int:" + i;
        case Long l -> "long:" + l;
        case Double d when Double.isNaN(d) -> "nan";
        case Double d -> "double:" + d;
        default -> "other";
    };
}

This stable example matches wrapper types. It does not claim that every Number can safely narrow to int.

2. Explain widening versus narrowing

JEP 507 regularizes primitive patterns so a pattern can match a primitive value when the conversion is safe for the pattern variable. Widening an int to long preserves every value. Narrowing a long to int can discard high bits, so a pattern that would silently accept such a conversion is unsafe. The answer should name the rejected conversion and propose an explicit range check if narrowing is a business requirement.

java
static String bucket(long value) {
    if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
        int narrowed = (int) value;
        return "small:" + narrowed;
    }
    return "wide:" + value;
}

The cast is now guarded by a visible invariant. A pattern is not a substitute for that proof.

3. Separate null from primitive matching

Primitive values cannot be null, but a boxed selector can. A switch over a reference selector does not silently make null a numeric case. Add case null when the domain requires a total result, or reject it before the switch. Do not use a default arm to hide an unexpected null or unknown wrapper; those cases have different operational responses.

For floating-point values, matching a double type does not mean the value is finite. Keep explicit checks for NaN, positive infinity, and negative infinity when downstream code assumes ordering or arithmetic.

4. Build a total switch deliberately

Primitive patterns and constants can make a switch concise, but coverage still depends on the selector domain and guards. A guarded case does not cover every value of its type. Use an unguarded case or default for the remainder, and include case null for nullable selectors. If the switch returns a value used for billing, parsing, or safety policy, make the fallback observable instead of silently accepting it.

5. Check preview boundaries

JEP 507 is preview in Java 25. Compilation and execution need the corresponding --enable-preview option, and the source/target release must match the selected JDK. Preview syntax can change or disappear in a later release. CI should compile a stable fallback as well, and an artifact should record the JDK, preview flag, and language level. A library should avoid forcing consumers to enable preview merely to call a public API.

6. Compare alternatives

Use explicit wrapper patterns when the input is a heterogeneous Number and compatibility matters. Use a primitive switch when the selector is already a primitive and the deployment can pin the preview toolchain. Use a validated conversion when the business rule is “fit in 32 bits,” because the range check communicates intent and survives a syntax change. Shorter source is not evidence of a safer numeric contract.

High-quality sample answer

“I would first state the selector and null policy. Java 25 JEP 507 is a preview feature, so I would not present it as stable syntax. Primitive patterns allow safer, more uniform matching across instanceof, switch, and record patterns, but they do not make narrowing lossless. Widening an int to long preserves values; converting a long to int needs an explicit range proof. For boxed input I handle null, distinguish wrapper types, and separately classify NaN and infinity. Every switch has an intentional remainder, and guarded cases do not provide total coverage. If production cannot pin Java 25 with preview enabled, I keep wrapper patterns or explicit checks and compile both paths in CI. Tests include null, every numeric boundary, duplicate representations, NaN, infinity, and a non-preview build.”

Common mistakes

  • Call preview syntax stable → a later JDK can change or remove it → pin the JDK and keep a fallback.
  • Assume every numeric conversion is safe → narrowing can lose bits or reject values → state the conversion and prove its range.
  • Let default absorb null → null and unknown types need different handling → add case null or reject before matching.
  • Treat a double match as finite → NaN and infinity still satisfy the primitive type → test and classify them explicitly.
  • Use a guarded case as exhaustive → values failing the guard remain unmatched → add an unguarded remainder and test coverage.

Follow-ups and responses

Can a long pattern safely bind an int selector?

Yes, widening preserves the int value. The reverse direction is not generally safe because a long may exceed the int range. If the domain guarantees a bound, encode the check explicitly and test both edges.

Why not just box every value?

Boxing can make heterogeneous inputs easier to inspect, but it adds allocation and wrapper semantics and does not prove that a conversion preserves value. Choose it for API compatibility, not as a way to avoid numeric reasoning.

How would you test the preview implementation?

Run the same cases against the preview implementation and the stable fallback: null, zero, minimum and maximum values, one-out-of-range values, NaN, both infinities, unknown wrappers, guarded-case misses, and malformed input. Compile with the exact release and preview flags in CI, then run a non-preview compatibility job.

What if Java 26 changes the preview rules?

Treat the language level as part of the artifact contract. Read the new release notes, compile the candidate branch, compare semantics with the fallback tests, and promote only after the source and runtime matrix passes. Do not silently enable a new preview flag in production.

Public sources

Related questions

Related interview tool

Use Screenshot for a coding prompt

Capture the problem, then work through the constraints, solution, code, edge cases, and complexity in order.

View the tool