Java interview bank
Questions interviewers actually ask, each answered at the length you would say it aloud, with the topic to reread if you were not sure.
Language core
21 questions · 0 Seen01 How do primitive and reference types differ in Java? reveal ▾ hide ▴
A primitive variable holds one of Java’s built-in Boolean, integral, or floating-point values and cannot hold null. A reference variable holds a reference to an object or array, or the null reference. Assigning a primitive copies its value. Assigning a reference copies the reference, so both variables can identify the same mutable object. Arrays are reference types even when their elements are primitive. Generic type arguments must be reference types, which is why a list of integers uses List
02 What can go wrong in Java widening, narrowing, and numeric promotion? reveal ▾ hide ▴
Widening to a wider integral type preserves the value, but widening int or long to floating point can lose precision. Narrowing integral conversions discard high bits, while floating-to-integral conversion rounds toward zero and has defined results for NaN and out-of-range values rather than throwing. Numeric promotion means byte, short, and char arithmetic usually runs as int. Intermediate expressions therefore matter: assigning an int multiplication to long does not prevent earlier overflow. Widen before the operation or use the Math exact methods when overflow must be rejected.
03 Why are boxing, wrapper equality, and null a dangerous combination? reveal ▾ hide ▴
Autoboxing converts a primitive to its wrapper and unboxing extracts the primitive. The conversions are often implicit in collections, arithmetic, comparisons, conditional expressions, and method calls. Unboxing null throws NullPointerException. Using == between wrapper references tests identity, and the required cache for certain small boxed constants can make that bug pass small-value tests. Use primitives when absence is impossible, validate nullable wrappers before arithmetic, and compare nullable wrapper values with Objects.equals. Also inspect overload selection because boxing, unboxing, and primitive widening affect which method is called.
06 How does Java select a catch clause and continue execution after an exception? reveal ▾ hide ▴
The JVM tests handlers around the throw site in source order and selects the first whose parameter can receive the exception object’s runtime type. If none matches, the current frame completes abruptly and the search continues in its caller. A subtype handler must precede its supertype or the later branch is unreachable. After a handler completes normally, execution continues after the entire try statement, not after the failed instruction. Rethrowing the same object preserves its identity and original trace; translating it should create a domain exception with the original as its cause.
09 How do scope and definite assignment differ for a Java local variable? reveal ▾ hide ▴
Scope says where a declared name can be referenced in source code. Definite assignment asks a separate control-flow question: has every path reaching this read already assigned the local? A variable can be in scope yet still be illegal to read. Fields and new array elements receive default values, but local variables do not. If one branch does not assign the local, that path must assign it later or leave before the read with return or throw. A final local adds the requirement that it be definitely unassigned before assignment.
14 Are Java records immutable, and what contract does a compact canonical constructor provide? reveal ▾ hide ▴
A record fixes its component fields as private and final and derives accessors plus value-oriented object methods, but that guarantee is shallow. A List, array, date-like object, or other mutable component can still change after construction. I use the canonical constructor to reject invalid values and establish ownership, often with List.copyOf or an array copy. A compact canonical constructor lets me validate or rebind parameters before the compiler assigns them to fields. I also review accessors, because returning a mutable component can expose internal state even when its field is final.
19 What correctness guarantees and boundaries matter in a Java 21 pattern switch over records and sealed types? reveal ▾ hide ▴
Record patterns test and deconstruct components, and a pattern switch selects the first applicable label. The compiler rejects a branch dominated by an earlier broader pattern and requires a switch expression to be exhaustive. For a sealed hierarchy I normally list every permitted branch and omit default so a newly added subtype fails at consumer recompilation. Null remains separate: case null handles it, otherwise the switch throws NullPointerException and default does not match it. Separate compilation still creates a binary-evolution boundary, so deployed old consumers need compatibility tests.
21 What contract does a Java record header define? reveal ▾ hide ▴
A record header declares the complete component state of a transparent data carrier. For each component, the compiler provides a private final field and a public same-named accessor. It also derives the canonical constructor, equals, hashCode, and toString from the component list. The record class is implicitly final and directly extends java.lang.Record, though it can implement interfaces and add behavior. Component names, types, and order are public API because callers, reflection, construction, object methods, and record patterns all observe that shape.
25 What contract does a sealed Java type establish, and what must each permitted subtype declare? reveal ▾ hide ▴
A sealed class or interface names the types allowed to extend or implement it directly. The permits list does not enumerate every descendant and does not limit how many instances exist. Each ordinary direct subtype must choose final to end its branch, sealed to control another permitted set, or non-sealed to reopen inheritance. Records are implicitly final. I use sealing when one module owns a finite domain model and adding a root variant should force consumer review. It is a compile-time hierarchy constraint, not authentication, authorization, or a sandbox.
29 How do immutability, equals, ==, and the string pool interact in Java? reveal ▾ hide ▴
A String never changes its code-unit sequence after construction, so transformations return a result and strings can safely keep stable hashes as map keys. equals compares content, while == compares whether two references identify the same object. The language interns equal literals and constant string expressions, which can make == appear to compare content in small tests. Runtime input and new String do not carry that assumption. I use equals or Objects.equals for value semantics and reserve == for deliberate identity checks. intern is a canonicalization tool, not a comparison method or default optimization.
31 When should Java code use +, StringBuilder, or StringBuffer? reveal ▾ hide ▴
I use + for a short expression because it states the result clearly and the compiler may optimize its implementation. I use StringBuilder when a loop or conditional process appends an unknown number of pieces, keeping the builder local and returning one final String. String.join or Collectors.joining is clearer when the work is only delimiter-based joining. StringBuffer synchronizes individual methods, but that does not make a multi-call read-modify-write protocol atomic. I rarely share a text buffer across threads; separate local builders usually express ownership better. Performance claims still require measurement on the target JDK and workload.
33 How do you design the target and retention of a custom Java annotation? reveal ▾ hide ▴
I start with the consumer and the phase where it reads metadata. A source or compiler tool may need SOURCE, a bytecode transformer may need CLASS, and reflection requires RUNTIME; CLASS is the default, so I never assume runtime visibility. I restrict Target to the exact declaration or type-use contexts the consumer scans, which lets javac reject meaningless placements. I also define what absence means, whether the annotation is public API, and tests that exercise the real consumer. Adding metadata without an active consumer does not add behavior.
37 When should a Java API use a named type parameter instead of a wildcard? reveal ▾ hide ▴
I use a named type parameter when the signature must preserve a relationship across two or more positions, such as an input element type and the return type, or a source and destination. I use a wildcard when one parameter has a constrained but otherwise irrelevant element type. List<?> is enough for observation, ? extends T describes a producer, and ? super T describes a consumer. A type parameter that appears only once often adds no useful relationship. Conversely, replacing every T with ? can erase information callers need. I validate the choice by listing the safe reads and writes for each parameter.
41 What is the precise difference between a nested class and an inner class in Java? reveal ▾ hide ▴
Nested class is the umbrella term for a class declared inside another class or interface: it can be a member, local, or anonymous class. An inner class is a nested class that is not explicitly or implicitly static. A static member class is therefore nested but not inner. Member and local records and enums, plus member classes of interfaces, are implicitly static. A normal local class and an anonymous class are inner classes, although one declared in a static context can have no immediately enclosing instance. I name both the declaration form and static status when explaining the relationship.
43 What does effectively final mean for a local variable captured by a local or anonymous class? reveal ▾ hide ▴
A captured local variable, parameter, or exception parameter must be final or effectively final. Effectively final means it omits the final modifier but obeys the assignment restrictions such that adding final would not introduce an error. The restriction applies to rebinding the variable, not to mutating the referenced object, so a captured List may receive add calls while the local name cannot later point to another list. I avoid one-element arrays used merely to bypass this rule. They hide shared mutable state; I put state and its invariants in a named owner and add synchronization only when the execution model requires it.
46 How does Java resolve inherited default methods? reveal ▾ hide ▴
A concrete instance method from a class or superclass takes precedence over an interface default. Among interface candidates, a declaration from a more specific subinterface overrides one from its parent. If unrelated interfaces contribute override-equivalent defaults and neither wins, the implementing class must override the method. Its body can select a direct superinterface implementation with InterfaceName.super.method(), combine both, or provide new behavior. Static and private interface methods do not enter this process because implementations do not inherit them. I verify a difficult case by drawing the full type graph and compiling a minimal example.
50 How do overloading, overriding, and dynamic dispatch interact in a Java method call? reveal ▾ hide ▴
The compiler first uses the receiver’s static type plus the argument types to choose an accessible method signature, which is where overload resolution happens. If that signature denotes an overridable instance method, runtime dispatch then selects the most specific override for the actual receiver object. Static, private, and final methods do not participate in ordinary overriding. Changing an argument variable to a wider static type can therefore select another overload without changing its runtime object. I add @Override to intended overrides and compile small ambiguous cases rather than reasoning from runtime types alone.
52 How do object identity, value equality, and hashing differ in Java? reveal ▾ hide ▴
Reference == asks whether two references identify the same object, while equals expresses the value relation promised by a type. Object starts with identity-based equals, but value types often override it. Equal objects must return the same hash code, so equals and hashCode are designed and tested together. Fields used in both should stay stable while an object is a hash key; otherwise lookup can fail after mutation. In inheritance hierarchies, adding subtype-only equality state can break symmetry or transitivity, which is why immutable final classes or records are often safer value objects.
53 How do `getMethod()` and `getDeclaredMethod()` differ, and how do you select an overload safely? reveal ▾ hide ▴
getMethod finds a public method in the type’s invocation view, including eligible inherited methods. getDeclaredMethod searches only the current class but can return any visibility. Neither performs source-level overload resolution: I must supply the exact declared parameter classes, so int differs from Integer and an implementation class does not match an interface parameter. I define whether the caller needs a public or declaration view, map external operation names to an allowlist of complete signatures, reject ambiguity, and test inherited, bridge, primitive, interface, varargs, and null cases.
57 What does Java type erasure remove, and what generic metadata can reflection still observe? reveal ▾ hide ▴
Erasure maps a type variable to its leftmost bound, or Object when it has no explicit bound, and maps a parameterized type to its raw class for JVM descriptors. That is why List
62 What can you safely read and write through each Java wildcard form? reveal ▾ hide ▴
From List<?> I can read Object and add no non-null value. From List<? extends T> I can read T, but I still cannot add a non-null T because the captured element type may be narrower. Through List<? super T> I can add T and its subtypes, while a read is only guaranteed as Object because the actual element type may be a supertype. Null is type-correct for every reference element type, but that says nothing about the collection or business null policy. These references may still support clear, remove, or reordering, so extends is not an immutability guarantee.
Numbers and text
1 question · 0 Seen04 How do you choose types for decimal money and Unicode text? reveal ▾ hide ▴
For money defined in decimal units, BigDecimal preserves decimal values and makes scale and rounding explicit. Construct it from decimal text or an exact integer, not from an already rounded double. Decide whether equality includes scale: equals distinguishes 1.0 from 1.00, while compareTo treats them as numerically equal. For text, char is one UTF-16 code unit, not every Unicode code point or displayed grapheme. Use String code-point APIs for supplementary characters, and use a text-boundary library when the requirement concerns user-perceived characters.
Error handling
2 questions · 0 Seen05 How do checked and unchecked exceptions differ, and when would you define each kind? reveal ▾ hide ▴
Checked exception classes are the Throwable hierarchy outside RuntimeException and Error. The compiler requires a caller to catch or declare them. I use a checked domain exception when callers should be forced to choose propagation, translation, or recovery, such as failing to load required external data. I use an unchecked exception for a violated precondition, illegal object state, or programming defect. Neither category dictates that every caller catch the failure. The type describes the contract; the catch boundary should exist only where code can apply a meaningful policy.
55 How should a reflection adapter classify and preserve invocation failures? reveal ▾ hide ▴
I separate lookup failure, denied access, incompatible receiver or arguments, and an exception thrown by target code. NoSuchMethodException, IllegalAccessException, and IllegalArgumentException identify the infrastructure stages. Method.invoke wraps a target exception in InvocationTargetException, so I inspect its cause and preserve it when translating to a stable domain exception or result. I do not return null for every failure because null may be a valid result. For proxies, I also check the interface throws clause because an undeclared checked exception can become UndeclaredThrowableException. Tests assert both the public classification and the original cause chain.
Resource management
1 question · 0 Seen07 What guarantees does try-with-resources provide when both work and cleanup fail? reveal ▾ hide ▴
Every successfully initialized, non-null resource receives a close attempt in reverse initialization order. A close failure does not prevent attempts to close the remaining resources. If the body already threw, that exception stays primary and later close failures are attached to it as suppressed exceptions. If the body succeeded, the first close failure becomes primary and later ones are suppressed onto it. I inspect getSuppressed as well as getCause during diagnosis. This differs from a cause: a cause explains why an exception was created, while suppression preserves concurrent cleanup failures.
Failure boundaries
1 question · 0 Seen08 What should an exception policy do at service, task, or API boundaries? reveal ▾ hide ▴
A boundary should classify expected domain failures, unexpected defects, cancellation, and failures that are safe to retry. It can translate low-level exceptions into stable domain types while retaining the original cause, then record the complete exception once where the request or task outcome is owned. Public responses should be sanitized and correlated with internal diagnostics rather than exposing paths, SQL, or credentials. Retry only transient failures of suitably idempotent operations. InterruptedException normally propagates; if the signature cannot do that, cleanup should finish and the thread’s interrupt status should be restored.
Expressions
1 question · 0 Seen10 What is the practical difference between && and & on Boolean operands? reveal ▾ hide ▴
Both can produce a Boolean conjunction, but && short-circuits: it evaluates the right operand only when the left operand is true. Boolean & always evaluates both operands. That difference controls side effects, exceptions, and safe guards such as value != null && value.isReady(). Java otherwise evaluates operands left to right. I use && for conditions and reserve & for deliberate non-short-circuit Boolean work or bitwise integer operations. Tests should make right-side execution visible, because identical truth tables can hide the behavioral difference.
Control flow
3 questions · 0 Seen11 How does a modern Java switch expression differ from a traditional switch statement? reveal ▾ hide ▴
A switch expression produces a value and must cover every selector value. An arrow rule does not fall through; it can provide an expression, a block, or throw. A block rule uses yield to provide the result. Traditional colon-labeled statement groups can fall through until break, which remains useful for compatible older code but is easy to change incorrectly. For enums or sealed hierarchies, the compiler may prove completeness without a source default. I still test binary evolution and null behavior at the API boundary rather than treating exhaustiveness as all-purpose validation.
12 How do you review a Java loop for correctness and termination? reveal ▾ hide ▴
I identify the initial state, the condition checked for each iteration, the update, and a measure that moves toward termination. For an array index, the usual invariant separates the processed half-open interval [0, index) from the unprocessed remainder, with index < length as the upper bound. I test empty and one-element inputs, the exact threshold, early break, and no-match behavior. I also inspect continue paths to ensure they do not skip a required update. Enhanced for is clearer when only element values are needed; explicit indexing is better when replacing slots.
16 How do Java 17 switch expressions differ from pattern matching for instanceof and from pattern switches? reveal ▾ hide ▴
A Java 17 switch expression is a stable construct that exhaustively produces a value; arrow rules do not fall through, and a block rule uses yield. Pattern matching for instanceof is also stable and binds a typed variable only where flow proves the match succeeded. Combining type patterns with switch was preview work in Java 17, not part of its stable language. The final pattern-switch syntax arrived later, in Java 21, and later examples may also use when guards or record patterns. For a Java 17 contract, I use enum switches and instanceof chains and enforce the boundary with —release 17.
Language evolution
2 questions · 0 Seen13 Which language features are available at the Java 17 source level, and which one actually became final in Java 17? reveal ▾ hide ▴
Stable Java 17 source can use switch expressions, text blocks, records, pattern matching for instanceof, and sealed classes. Their final releases were 14, 15, 16, 16, and 17 respectively, so only sealed classes became final in Java 17 itself. I keep that provenance separate from the project baseline because it prevents inaccurate minimum-version claims. I also compile with javac —release 17: running a newer JDK does not stop generated source from using Java 21 syntax such as final pattern switches or record patterns.
17 Which major Java 21 application features are stable, and which release-note features must not be treated as stable Java 25 APIs? reveal ▾ hide ▴
Virtual threads, record patterns, pattern matching for switch, and sequenced collections are final Java 21 features and require no preview flag. Structured Concurrency and String Templates were previews in Java 21. Structured Concurrency continued through a fifth preview in Java 25 and its API changed, while the later String Templates proposal was withdrawn and the syntax is absent from Java 25. I enforce a stable Java 21 contract with javac —release 21 and no —enable-preview, rather than inferring stability from a release-note list.
Type design
1 question · 0 Seen15 What does a sealed hierarchy guarantee in Java 17, and where does that guarantee stop? reveal ▾ hide ▴
A sealed class or interface controls its permitted direct subtypes. Each direct subtype must be final, sealed, or non-sealed, with records implicitly final. In a named module those subtypes must share the module; in an unnamed module they must share the package. The guarantee stops at a non-sealed branch, whose descendants are open again. That makes sealed types good for domain alternatives owned by one module, but usually wrong for cross-module plugin contracts. In stable Java 17 I consume the hierarchy with polymorphism or instanceof patterns, not a final type-pattern switch.
Concurrency
1 question · 0 Seen18 Why should virtual threads usually be created per task, and how do you limit a scarce downstream resource? reveal ▾ hide ▴
A virtual thread is a cheap representation of a blocking task, so a thread-per-task executor avoids sizing and queueing a worker pool. It does not create database connections, remote quota, file descriptors, heap, or CPU capacity. I let each independent blocking task have a virtual thread, then gate only the scarce operation with a semaphore, connection pool, or rate limiter sized from the resource contract. CPU-bound work remains limited by processor parallelism. I also test interruption, timeouts, cancellation, and ThreadLocal memory at realistic concurrency.
Collections
1 question · 0 Seen20 What contract does a Java 21 sequenced collection provide, and why is reversed() often misunderstood? reveal ▾ hide ▴
A sequenced collection has a defined encounter order and uniform first, last, and reverse-order operations. The concrete collection still decides which mutations it supports, so addFirst can throw on an unmodifiable or structurally constrained implementation. reversed() returns a backed view rather than a snapshot: changes to the original are visible through the view, and supported view mutations write through. I name it reverseView, document ownership, and copy explicitly when a caller needs an independent snapshot. Copying the container still does not deep-copy mutable elements.
Object design
6 questions · 0 Seen22 How should a record canonical constructor establish invariants and ownership? reveal ▾ hide ▴
I use the canonical constructor as the one path that validates every component and takes ownership of mutable inputs. A compact constructor omits the parameter list, lets me validate or rebind implicit component parameters, and relies on compiler assignments after its body. I normalize values there rather than in accessors, preserving the copy invariant. Lists usually become List.copyOf results; arrays require explicit copies, often at both construction and access. An auxiliary constructor must delegate with this so it cannot bypass the canonical path. Element mutability still needs a separate policy.
23 Why are mutable and array components dangerous in record equality and hashing? reveal ▾ hide ▴
Derived equality and hashing use every component field. A mutable list or map can change after construction, changing the record hash and breaking lookup when the record is stored as a HashMap key. Final only fixes the field reference; it does not freeze the referenced object. Arrays add another trap because their inherited equals and hashCode are identity based, not content based. I prefer immutable value components, make defensive copies, and use an unmodifiable List for logical sequences. If an array is unavoidable, I design equals and hashCode together and test their contract.
42 How do you choose between a static nested class and a member inner class? reveal ▾ hide ▴
I ask whether every helper instance inherently belongs to one outer object and needs implicit access to that object’s state. If yes, a member inner class can express that ownership, and callers create it with outer.new Inner(). If the helper needs only explicit constructor data, I make it static. That removes the implicit enclosing-instance relationship and makes lifetime easier to see. Static does not prohibit access to private instance members through an explicit outer reference. I also inspect long-lived listeners, caches, and executor tasks, because an inner object stored there can keep its outer object graph reachable.
45 When would you choose a Java interface over an abstract class? reveal ▾ hide ▴
I choose an interface when callers need a behavior contract that unrelated classes can implement, or when one class must expose several independent capabilities. I choose an abstract class when implementations share per-instance fields, constructor rules, protected helpers, or a common lifecycle. Default methods let an interface derive convenience behavior from existing operations, but they do not give it instance state. I start from the caller’s smallest useful contract and avoid creating an interface merely to mirror every public method of one class. Contract tests then run against each substitute implementation.
49 What does encapsulation protect beyond making fields private? reveal ▾ hide ▴
Encapsulation assigns ownership of representation and state transitions to one object. I start by writing its invariants, then make construction establish them and ensure every public mutation preserves them. Private fields help, but unrestricted setters, returned mutable collections, and retained aliases can still bypass the boundary. I prefer operations such as withdraw or reschedule that express one valid domain transition. For arrays and collections, I define ownership and copy where required. I test invalid construction, every mutation boundary, and attempts to change state through input or output aliases.
51 When should you replace inheritance with composition? reveal ▾ hide ▴
I use inheritance only when the subtype can preserve every caller-visible promise of the base type: accepted inputs, result meaning, exceptions, side effects, invariants, and lifecycle. Reusing implementation is not enough. If a candidate subtype must reject valid base inputs or reinterpret an operation, substitution is already broken. Composition is usually better when I need a replaceable policy, optional capability, or reusable helper because the dependency stays explicit and can vary independently. I validate the decision with one base-type contract test suite run against every implementation, not with a class diagram alone.
Pattern matching
2 questions · 0 Seen24 What should you verify when using record patterns with a sealed hierarchy? reveal ▾ hide ▴
A record pattern checks the record type and invokes component accessors to feed nested patterns. Null matches no record pattern, so a pattern switch needs case null or an explicit non-null boundary. Branch order matters because an earlier broad pattern can dominate a later one. With a sealed hierarchy, listing every permitted subtype lets the compiler check exhaustiveness when the consumer is recompiled; a broad default can hide omissions. I also compile with the promised —release because record patterns became final in Java 21, and I test separately deployed binaries for hierarchy evolution.
26 How do sealed types improve switch exhaustiveness, and why can default be harmful? reveal ▾ hide ▴
For a switch over a sealed selector, the compiler calculates type coverage from the permitted direct subtypes. I normally list each closed branch and omit default, so adding a root variant makes consumers fail when recompiled and identifies the code that needs review. A non-sealed branch is covered by a pattern for that branch type, not by enumerating its current implementations. Null is separate: case null handles it; otherwise a null selector throws NullPointerException, and default does not catch it. Separate binaries still need compatibility tests because old consumers are not recompiled automatically.
Modules and packaging
2 questions · 0 Seen27 What location and inference rules constrain permitted direct subtypes? reveal ▾ hide ▴
When a sealed root is in a named module, every directly permitted subtype must be in that same module, although packages may differ. In an unnamed module, those subtypes must share the root package. A permits entry names a direct subtype that is accessible to the root; grandchildren belong to their direct sealed parent instead. If permits is omitted, the compiler infers direct subtypes declared in the same compilation unit, not everything in the package. Moving one inferred subtype to another source file therefore requires adding an explicit permits clause. I test these rules with the production module path and —release.
54 What determines whether Java reflection may access a non-public member in a named module? reveal ▾ hide ▴
Ordinary reflection starts with Java access rules and module readability and exports. Suppressing those checks also depends on the relationship between the caller module and the target package, especially whether that package is open to the caller. trySetAccessible reports failure as false, while setAccessible(true) throws InaccessibleObjectException when access cannot be enabled. I prefer a public API; when a framework genuinely needs deep reflection, the owning module declares a qualified opens contract. I test on the production module path without accidental global --add-opens flags and treat access failure as deployment configuration, not missing data.
API evolution
3 questions · 0 Seen28 How would you evolve a published sealed hierarchy safely? reveal ▾ hide ▴
Adding a permitted root subtype is source incompatible for exhaustive switches: recompiled consumers must add a branch. Already compiled consumers do not receive that check, and an enhanced switch with no applicable label can throw MatchException when old consumer code sees the new subtype. I test a new producer with both recompiled and still-deployed old consumers. I also inspect reflection, serialization, framework proxies, and protocol mappings because PermittedSubclasses metadata does not solve those compatibility concerns. If a compatibility window needs fallback behavior, I put it at a controlled version boundary rather than adding default everywhere.
36 How do you evolve a published annotation interface safely? reveal ▾ hide ▴
I treat it as API used by source callers, processors, runtime readers, and existing class files. A new required element breaks old source when recompiled, so a new element normally needs a semantically safe default. Defaults are read from the current annotation interface rather than copied into each use, which preserves readability of old binaries but can still change behavior. Removing an element, changing its type, narrowing Target, or changing Retention needs compatibility analysis. I test old and new source, processors, and mixed binary versions, including access to every element rather than merely discovering the annotation.
48 How do you evolve a published Java interface safely? reveal ▾ hide ▴
I separate source, binary, and behavioral compatibility. A new abstract method breaks implementing source on recompilation; a new default can preserve many old binaries but still collide with another superinterface or change behavior. I cleanly rebuild all available implementations and callers, then run old implementation binaries with the new interface to exercise mixed deployment. Contract tests compare results, exceptions, side effects, null rules, and concurrency guarantees. I also inspect functional-interface status and compile-time constants, because a second abstract method breaks lambda targets and old caller binaries may retain an inlined constant value.
Text and Unicode
1 question · 0 Seen30 What does String.length count, and how do you truncate Java text safely? reveal ▾ hide ▴
String.length counts UTF-16 code units, and charAt and substring use offsets in that same unit. A supplementary Unicode code point occupies a surrogate pair, so an in-range substring can still split it. For a code-point limit, I count with codePointCount and convert the chosen count to an endpoint with offsetByCodePoints. User-visible characters are a different contract because one grapheme cluster can contain several code points. UI truncation therefore needs a suitable Unicode text segmenter. I test BMP text, supplementary characters, combining marks, empty input, zero, and the exact limit.
API boundaries
1 question · 0 Seen32 Which hidden contracts should you make explicit when Java strings cross system boundaries? reveal ▾ hide ▴
I specify the charset for every byte boundary, usually the protocol-defined UTF-8, and choose whether malformed input is rejected or replaced. I distinguish null, empty, and blank values rather than normalizing them accidentally. For machine keys I state locale and Unicode normalization rules; natural-language casing uses the content locale. I also check whether an API interprets literal text or regex syntax. split takes a regex and drops trailing empty fields by default, while replaceAll has separate replacement metacharacters. Boundary tests include non-ASCII text, trailing empties, regex characters, dollar signs, backslashes, and malformed bytes.
Reflection
1 question · 0 Seen34 How do Java reflection queries differ for direct, inherited, and repeatable annotations? reveal ▾ hide ▴
Declared queries inspect metadata directly on the element. On classes, non-declared queries can also follow the superclass rule defined by Inherited, but that rule does not traverse interfaces or copy annotations onto overridden methods. Repeatable annotations add another distinction: singular getAnnotation can return null when multiple uses are stored in their container, while getAnnotationsByType unfolds the container. I choose the query from the framework contract rather than by habit, then test zero, one, and multiple uses plus superclass, interface, and override boundaries. Type-use metadata requires AnnotatedType APIs instead.
Compilation
3 questions · 0 Seen35 When should you use annotation processing instead of runtime reflection? reveal ▾ hide ▴
I use processing when a decision can be made from the compile-time language model and should produce a diagnostic, source, or resource before deployment. Processors work with Element and TypeMirror in rounds, so they can inspect source types that do not yet have loadable Class objects. Reflection fits behavior that must inspect RUNTIME metadata from loaded program elements. In Java 25, I configure processing explicitly with a processor path, module path, processor name, or proc option; placing a processor only on the ordinary class path is not enough to express that intent. Clean-build tests verify generated outputs.
58 Why can erasure reject one pair of methods yet cause the compiler to generate another method? reveal ▾ hide ▴
Two declarations such as read(List
64 What is wildcard capture, and what can a capture helper prove? reveal ▾ hide ▴
Capture conversion gives one wildcard expression a fresh internal type, often printed as CAP#1 in javac diagnostics. A private generic helper can name that type as T, which makes operations such as reading an element from one List<?> and writing it back to the same list type-safe. Capture does not erase the bound and does not inspect runtime elements. Two separate wildcard expressions normally receive separate captures, so a helper cannot justify moving a value between List
API design
2 questions · 0 Seen38 Why is `List<Integer>` not a subtype of `List<Number>`, and how does PECS help? reveal ▾ hide ▴
Java generic types are invariant. If List
63 When should an API use a named type parameter instead of a wildcard? reveal ▾ hide ▴
I use a wildcard when one parameter has an element type that is constrained but irrelevant by name, such as List<?> for observation or Iterable<? extends T> for a source. I declare
Runtime model
1 question · 0 Seen39 What does type erasure remove, and which generic type information can reflection still see? reveal ▾ hide ▴
Erasure maps a type parameter to its leftmost bound, or usually Object, and parameterized instantiations share one runtime class. That is why an ordinary ArrayList
Code review
1 question · 0 Seen40 How do you review an unchecked generic cast or `@SafeVarargs` claim? reveal ▾ hide ▴
I treat the warning as a proof obligation, not compiler noise. First I trace every value that can enter the object through raw aliases, reflective construction, deserialization, array covariance, or varargs. Then I state the invariant that makes the cast safe and keep both the cast and suppression in the smallest helper. For generic varargs, @SafeVarargs is valid only as a promise that the implementation does not expose the array or use it to create incompatible writes; the annotation does not verify that promise. I compile with -Xlint:all, test adversarial element types, and remove the suppression if no local argument proves safety.
Callbacks
1 question · 0 Seen44 When is an anonymous class not equivalent to a lambda expression? reveal ▾ hide ▴
A lambda can target only a functional interface and does not create a new this scope; this inside it refers to the enclosing object. An anonymous class creates its own object and this, can extend an ordinary class, and may declare fields, instance initializers, and extra methods. Those differences matter when code uses identity, qualified this, overload resolution, or per-callback state. Before converting, I identify the target type and every use of this or extra members, then run the callback after registration rather than only at construction time. Concision alone is not evidence that behavior is preserved.
Functional programming
1 question · 0 Seen47 What exactly makes an interface functional, and why use `@FunctionalInterface`? reveal ▾ hide ▴
A functional interface has an abstract method set that yields one function descriptor after Java applies override-equivalence, return-type substitutability, and the special handling of public Object methods. It may still declare default, static, and private methods because those do not create another abstract contract. A lambda or method reference uses the function descriptor as its target type. The annotation is not required for lambda conversion, but it asks the compiler to reject accidental changes, such as adding an unrelated second abstract method. I keep it on interfaces intentionally designed as callback or strategy targets.
Runtime architecture
1 question · 0 Seen56 What contract must an `InvocationHandler` define for a JDK dynamic proxy? reveal ▾ hide ▴
The handler must define dispatch for the interface methods and also for equals, hashCode, and toString, which arrive as Object declarations. It handles a possibly null argument array for no-argument calls, decides how default interface methods are invoked, and unwraps InvocationTargetException when delegating reflectively. I state whether object methods use proxy identity, target semantics, or wrapper semantics and test equality symmetry. The selected loader must see every proxy interface, including the same interface identity used by callers. I also document interceptor order for logging, retries, transactions, and authorization because changing that order changes observable behavior.
Type system
2 questions · 0 Seen59 Why can Java create `List<?>[]` but not `List<String>[]`? reveal ▾ hide ▴
Arrays enforce their component type at runtime, so an array creation expression requires a reifiable component type. List<?> is reifiable because runtime checking needs to prove only that an element is some List. List
61 Why is `List<Integer>` not a subtype of `List<Number>`, and how does PECS recover safe flexibility? reveal ▾ hide ▴
Java generic types are invariant. If a List
Debugging
1 question · 0 Seen60 How do you find the source of heap pollution when `ClassCastException` occurs much later? reveal ▾ hide ▴
I treat the throwing read as the endpoint, not necessarily the cause. The compiler inserted that cast because the receiving expression expected a parameterized element. I rebuild with unchecked and varargs warnings enabled, then trace raw assignments, unchecked casts, reflective results, deserialization boundaries, and generic arrays upstream. Every suppression must name the invariant that keeps the operation safe. A useful regression test inserts a value of the wrong type at the suspected boundary and follows the complete consumer path; merely asserting that the unchecked cast itself succeeds proves nothing.
No questions match this filter.