# Java rules

Apply these rules to every relevant file in this project.

- Generating `@RequiresRole("admin")`, `@Transactional`, or `@NonNull` doesn't establish authorization, transactions, or null checks.
  Why: If the relevant consumer isn't installed, enabled, or present on that call path, program behavior doesn't change.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- Do not assume this is safe: a custom annotation without `@Retention` enters the class file but isn't visible to `getAnnotation()` at runtime.
  Why: Code often mistakes the resulting `null` for “no restriction,” turning configuration failure into a security problem.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- `getDeclaredAnnotation()`, `getAnnotation()`, and `getAnnotationsByType()` have different search and container-expansion rules.
  Why: A mechanical singular query misses repeatable annotations, while a mechanical inheritance query can misrepresent superclass configuration as directly declared on the subclass.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- Omitting `@Target` permits many locations with no consumer semantics, while class-level `@SuppressWarnings` hides new problems across the class.
  Why: A broad scope looks convenient but removes constraints the compiler could enforce.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- Putting a processor JAR only on the ordinary class path doesn't guarantee that Java 25 `javac` will execute it.
  Why: The build may succeed without explicitly configured processing while omitting required generated code or diagnostics.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- Do not assume this is safe: adding an element without a default makes existing source uses omit a required value when recompiled.
  Why: Removing an element, changing its type, or narrowing its target can also break source, processors, or reads of existing binary metadata.
  Source: [Annotations](https://codewiki.com/java/annotations/)
- Do not assume this is safe: applying field and array default rules to a local variable produces code that does not compile.
  Why: Fix it by explicitly initializing the local on every control-flow path. Do not insert an arbitrary `0` or `false` merely to satisfy the compiler unless that value represents a valid state.
  Source: [Data types](https://codewiki.com/java/data-types/)
- Do not assume this is safe: storing a result in `long` does not make an earlier `int` multiplication run with 64-bit range, and an explicit narrowing cast does not validate the range.
  Why: Widen before the operation and use `Math.addExact`, `multiplyExact`, `toIntExact`, or an explicit boundary check when the domain rejects wraparound.
  Source: [Data types](https://codewiki.com/java/data-types/)
- Representing money with `double`, then comparing every result with one fixed epsilon, hides a decimal rounding policy inside incidental error.
  Why: Use `BigDecimal` constructed from decimal text and state the scale and `RoundingMode`. For measurements, derive the tolerance from the domain's scale rather than a universal constant.
  Source: [Data types](https://codewiki.com/java/data-types/)
- Do not assume this is safe: wrapper `==` can pass with small test integers and fail with other values.
  Why: Calling `equals` on `null` or triggering unboxing also throws. Use `Objects.equals` for nullable wrapper values and define null, default, and rejection policies at the API boundary.
  Source: [Data types](https://codewiki.com/java/data-types/)
- Do not treat one `char` as one character splits surrogate pairs for supplementary Unicode code points and; doing so can miscount text.
  Why: First decide whether the requirement concerns UTF-16 code units, Unicode code points, or user-perceived graphemes. Use the `String` code-point APIs for code-point processing.
  Source: [Data types](https://codewiki.com/java/data-types/)
- Do not assume this is safe: catching `Exception` and returning `null`, an empty collection, or a default collapses “no result” and “operation failed” into one state, while also hiding defects such as `NullPointerException`.
  Why: Catch only a type this layer can recover from or translate; otherwise let it propagate and keep the return type truthful about normal results.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- Do not assume this is safe: constructing a replacement exception without passing the original breaks the cause chain, as in `throw new OrderLoadException("load failed")`.
  Why: Provide a constructor that accepts `Throwable cause` and calls `super(message, cause)`, and pass the complete exception object to logging rather than only its message.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- Do not assume this is safe: closing several resources manually in `finally` can replace the body failure, skip a resource, or swallow a closing failure.
  Why: Make the resources `AutoCloseable` and use try-with-resources; when diagnosing a failure, inspect both `getCause()` and `getSuppressed()` on the primary exception.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- Do not treat a caught `InterruptedException` as an ordinary timeout clears a cancellation signal and breaks the owner's stop policy.
  Why: Prefer propagation; when the signature cannot propagate it, complete necessary cleanup, call `Thread.currentThread().interrupt()`, and then return or raise a domain failure as the method contract requires.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- Do not assume this is safe: returning from `finally`, or throwing a new exception there without a cause, replaces a result already produced by `try` or `catch`.
  Why: Keep `finally` limited to bounded cleanup and do not return from it; let try-with-resources manage resources whose cleanup may fail.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- “Log and rethrow” at every layer produces several records for one failure, while an empty `catch` makes the failure disappear.
  Why: Log the complete exception once at the boundary that owns a request, task, or process outcome; catch in intermediate layers only to recover or add domain meaning.
  Source: [Exceptions](https://codewiki.com/java/exceptions/)
- Assigning `List` to `List` because `Integer extends Number` would theoretically let the receiver write a `Double`.
  Why: The compiler rejects the assignment precisely to protect the original list.
  Source: [Generics](https://codewiki.com/java/generics/)
- Replacing `List` with raw `List`, or adding an unexplained `(List)` cast, only moves an error from compilation to a later read.
  Why: Generated code often adds `@SuppressWarnings("unchecked")` at the same time and hides the evidence.
  Source: [Generics](https://codewiki.com/java/generics/)
- Elements of `List` can safely be read as `Number`, but the actual element type might be `Integer`.
  Why: Adding a `Double`, an `Integer`, or even an ordinary `Number` isn't safe; the only universally permitted value is `null`.
  Source: [Generics](https://codewiki.com/java/generics/)
- After erasure, an ordinary `ArrayList` object carries no `String` argument for an `instanceof` check.
  Why: Guessing `T` from `value.getClass()` fails for empty containers, subclasses, proxies, and data assembled from mixed sources.
  Source: [Generics](https://codewiki.com/java/generics/)
- Arrays know their component type at runtime and are covariant, while generics are usually erased and invariant.
  Why: `new T[]` and `new List[10]` are forbidden, and a cast-created generic array can still cause heap pollution.
  Source: [Generics](https://codewiki.com/java/generics/)
- Do not treat a static nested class as one of “four inner classes”; doing so makes later reasoning about enclosing instances and `this` contradictory.
  Why: Implicit `static` on records, enums, and interface members adds further confusion.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- An instance of a non-static member inner class is associated with an outer instance.
  Why: If the inner object enters a cache, listener registry, executor queue, or another long-lived container, the outer object and its graph may remain reachable too.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- Generated code often changes a local counter into `int[] count = {0}` or an `AtomicInteger` solely so an anonymous class can modify it.
  Why: The array hides shared mutable state, while one atomic operation doesn't make a whole business transaction atomic.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- A lambda works only for a functional interface and doesn't establish a new `this`.
  Why: An anonymous class can extend a class and declare fields or extra methods, so shorter syntax doesn't prove equivalent behavior.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- Do not assume this is safe: `Outer$1`, `Outer$1Local`, and `this$0` are common `javac` artifacts, not stable source contracts for application code.
  Why: Reordering or adding an anonymous class can break reflection, configuration, or serialization logic based on those names.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- Do not assume this is safe: older material says an inner class may declare only static constants, not ordinary static members or static initializers.
  Why: Java 16 relaxed that restriction, so applying it to Java 25 code produces bogus fixes.
  Source: [Inner classes](https://codewiki.com/java/inner-classes/)
- Interface fields are implicitly `public static final`, but `final` prevents reassignment only.
  Why: `List EVENTS = new ArrayList<>()` is still a public global list that every implementation and caller can mutate.
  Source: [Interfaces](https://codewiki.com/java/interfaces/)
- An interface that requires reads, writes, deletion, bulk export, and event subscriptions forces a read-only implementation to throw `UnsupportedOperationException`.
  Why: The type says an operation is available, but runtime behavior refuses it, so substitutability is already broken.
  Source: [Interfaces](https://codewiki.com/java/interfaces/)
- Generated code often catches `Exception` in a default method and returns `null`, an empty collection, or a success status.
  Why: Every implementation gets shared code, but it also gets a retry, logging, or fallback policy that may be wrong for its context.
  Source: [Interfaces](https://codewiki.com/java/interfaces/)
- Do not assume this is safe: adding a default often lets old implementations run without an immediate rebuild, but it can turn a same-signature class method into an override or collide with a method from another interface.
  Why: Binary, source, and behavioral compatibility are separate questions.
  Source: [Interfaces](https://codewiki.com/java/interfaces/)
- Do not assume this is safe: if `Parser.create()` is declared in an interface, `JsonParser.create()` does not become available merely because `JsonParser implements Parser`.
  Why: Looking for the method through the implementation type fails to compile and obscures which API owns it.
  Source: [Interfaces](https://codewiki.com/java/interfaces/)
- Reading “Java 17 features” as “five constructs first released in Java 17” makes release notes and minimum-version declarations inaccurate.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- AI output and newer tutorials often generate `switch (value) { case Order order -> ...
  Why: }`, sometimes with a `when` guard, and label it stable Java 17 code.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- Do not assume this is safe: record component fields are `final`, but a record does not make a referenced `List`, array, or domain object immutable.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- Adding a casual `default` to a switch over a closed enum lets a new enum constant compile and fall into a vague old branch.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- Do not assume this is safe: a text block improves layout but does not escape dynamic data for JSON, HTML, shell code, or SQL.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- Do not assume this is safe: sealing an extension-facing plugin interface, or assuming all descendants remain known after a permitted branch becomes `non-sealed`, creates a false closed model.
  Source: [Java 17 features](https://codewiki.com/java/java17-features/)
- Copying `STR."Hello, \{name}"` or an early `StructuredTaskScope` example from Java 21 release material and treating it as stable Java 25 API causes compilation failure or version lock-in.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- Do not assume this is safe: putting virtual threads in a fixed-size pool or expecting them to accelerate a CPU-bound loop reintroduces queueing without adding compute capacity.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- Attaching a large `ThreadLocal` cache to every virtual thread can turn “threads are cheap” into memory pressure that grows linearly with tasks.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- Do not assume this is safe: old advice says “virtual threads pin in `synchronized`, so replace it all with `ReentrantLock`.” That has context for long blocking critical sections on Java 21, but it is not a general rule after Java 24.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- A broad `default` in an exhaustive sealed-type switch lets a newly permitted type fall silently into old behavior after recompilation.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- Do not treat `reversed()` as a snapshot lets later mutations of the original alter a cached response, output, or audit result unexpectedly.
  Source: [Java 21 features](https://codewiki.com/java/java21-features/)
- Applying field and array-element defaults to local variables.
  Why: `int count; System.out.println(count);` does not print `0`; it fails to compile.
  Source: [Java language fundamentals](https://codewiki.com/java/fundamentals/)
- Comparing the business values of two strings or wrapper objects with `==`.
  Why: Constants in a test may happen to share an object, making reference comparison look correct until runtime input arrives.
  Source: [Java language fundamentals](https://codewiki.com/java/fundamentals/)
- Do not assume this is safe: looking only at the receiving variable's type, not the intermediate expression.
  Why: `long total = unitPrice * quantity` can first overflow as `int` when both operands are `int`.
  Source: [Java language fundamentals](https://codewiki.com/java/fundamentals/)
- Writing an array loop bound as `index <= values.length`, or changing the index in several branches of the loop body.
  Why: These errors often appear only with an empty array or on the final iteration.
  Source: [Java language fundamentals](https://codewiki.com/java/fundamentals/)
- Do not assume this is safe: mixing traditional colon `switch` groups with arrow rules and assuming a colon branch stops automatically.
  Why: A missing `break` continues into later statement groups, a bug generated code often introduces when adding a case.
  Source: [Java language fundamentals](https://codewiki.com/java/fundamentals/)
- Mechanically generating a getter and setter for every field reduces an object to a data bag that outside code may rewrite freely.
  Why: Independent setter validation can also permit invalid cross-field combinations, such as an end time before a start time.
  Source: [Object-oriented programming](https://codewiki.com/java/oop/)
- Do not assume this is safe: a subtype inherits visible API, lifecycle assumptions, and override points, not just a few implementation lines.
  Why: If it must reject inputs the base accepts or change result units and side effects, it breaks the substitution contract.
  Source: [Object-oriented programming](https://codewiki.com/java/oop/)
- When a base constructor calls an overridable instance method, dynamic dispatch can enter the subtype before its fields finish initialization.
  Why: Generated code often creates an “initialization hook” this way, then reads default values or leaks a partially constructed `this`.
  Source: [Object-oriented programming](https://codewiki.com/java/oop/)
- Compile-time types choose an overload.
  Why: Assigning an argument to a wider base-type variable can select a different overload even though the runtime object remains the same subtype; this isn't dynamic polymorphism.
  Source: [Object-oriented programming](https://codewiki.com/java/oop/)
- Overriding `equals()` for values while retaining an identity-based `hashCode()` sends equal objects to different hash buckets.
  Why: Including mutable fields in both can also make an object disappear from lookup after it becomes a `HashMap` key.
  Source: [Object-oriented programming](https://codewiki.com/java/oop/)
- Calling a record “immutable” while a component directly references a list, map, array, or date object that its caller can still mutate.
  Source: [Records](https://codewiki.com/java/records/)
- Do not assume an array component participates in default `equals()` and `hashCode()` by content like a list does.
  Source: [Records](https://codewiki.com/java/records/)
- Overriding a component accessor to mask, convert, or calculate a different value on demand.
  Source: [Records](https://codewiki.com/java/records/)
- Generating setters, extra instance fields, or a no-argument half-object constructor to satisfy old JavaBean assumptions.
  Source: [Records](https://codewiki.com/java/records/)
- Do not assume this is safe: using record patterns at the wrong source level, or hiding missing sealed-hierarchy branches behind `default`.
  Source: [Records](https://codewiki.com/java/records/)
- Do not assume this is safe: `argument.getClass()` turns `int` into `Integer`, retains an implementation class instead of the declared interface, and fails for a `null` argument.
  Why: It can't reproduce compiler overload resolution.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- Replacing every query with `getDeclaredMethod()` to reach private members makes inherited public methods disappear.
  Why: Replacing in the other direction misses non-public declarations on the current class.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- Module boundaries introduced in Java 9 can reject deep reflection; Java 25 doesn't open `java.base` or a third-party module merely because code called `setAccessible(true)`.
  Why: Successfully suppressing a language check isn't business authorization either.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- `InvocationTargetException` wraps an exception from the target method.
  Why: Logging only the wrapper message, returning `null`, or throwing a cause-free `RuntimeException` deletes the real failure type, stack, and recovery information.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- The order of `getDeclaredMethods()` isn't a registration priority, and its results may include synthetic and bridge methods.
  Why: Choosing the first array element lets the build artifact change conflict results.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- A process-wide `Map, Method>` strongly references classes, members, and the class loaders that define them.
  Why: In reloadable plugins or application servers, that can keep obsolete versions reachable after unload.
  Source: [Reflection](https://codewiki.com/java/reflection/)
- Do not treat `permits` as a complete list of all descendants produces duplicate or illegal declarations.
  Why: A root lists only direct subtypes; a branch that remains sealed lists its own direct subtypes in its own `permits` clause.
  Source: [Sealed classes](https://codewiki.com/java/sealed-classes/)
- Mechanically adding `non-sealed` to remove a compilation error turns that branch into a permanent extension point.
  Why: Arbitrary descendants can then appear without changing the root, and root consumers cannot enumerate them individually.
  Source: [Sealed classes](https://codewiki.com/java/sealed-classes/)
- Adding `default` to the end of a switch over a sealed hierarchy hides new variants.
  Why: The code keeps compiling, but a new type can fall into stale fallback behavior.
  Source: [Sealed classes](https://codewiki.com/java/sealed-classes/)
- Do not assume this is safe: placing a permitted subtype in another package of the unnamed module fails compilation.
  Why: The rule is not “always the same package”: a named module permits different packages within that module.
  Source: [Sealed classes](https://codewiki.com/java/sealed-classes/)
- Do not treat a sealed hierarchy as a security boundary misses the real runtime risks.
  Why: Whether reflection, deserialization, or input data is trusted is separate from which classes the compiler permits to extend a type directly.
  Source: [Sealed classes](https://codewiki.com/java/sealed-classes/)
- Comparing string contents with `==` mistakes string-pool reuse for value equality.
  Why: Use `equals()` for non-null strings or `Objects.equals()` when a reference may be `null`, and include runtime-constructed equal strings in tests.
  Source: [Strings](https://codewiki.com/java/strings/)
- Do not treat `null`, the empty string `""`, and a whitespace-only string as the same state; doing so makes validation and serialization rules conflict.
  Why: Define missing, empty, and blank values separately at the API boundary, then use a null check, `isEmpty()`, or `isBlank()` according to that contract.
  Source: [Strings](https://codewiki.com/java/strings/)
- Using `length()` or `substring()` to truncate by “characters” can split a surrogate pair or grapheme cluster.
  Why: Name the unit as code units, code points, or grapheme clusters, use the corresponding offset or segmentation API, and test supplementary code points and combining sequences.
  Source: [Strings](https://codewiki.com/java/strings/)
- Do not assume this is safe: `getBytes()`, `new String(bytes)`, and no-argument case conversion depend on environmental defaults, so another machine can produce a different result.
  Why: Use an explicit charset for protocols and `Locale.ROOT` for machine identifiers; pass the user's or content's locale for natural-language conversion.
  Source: [Strings](https://codewiki.com/java/strings/)
- `split()` takes a regular expression and drops trailing empty strings by default; replacement text passed to `replaceAll()` also interprets `$` and backslashes.
  Why: Use `Pattern.quote()` for a literal delimiter, a negative split limit when trailing fields matter, and `Matcher.quoteReplacement()` for literal replacement text.
  Source: [Strings](https://codewiki.com/java/strings/)
- Do not assume this is safe: repeated `result += part` in a loop keeps creating immutable results, while sharing one `StringBuffer` does not make a whole multistep protocol atomic.
  Why: Keep a `StringBuilder` local and build once; if state must cross threads, design ownership and synchronization around the entire operation.
  Source: [Strings](https://codewiki.com/java/strings/)
- `value instanceof List` checks only the raw runtime class.
  Why: Casting the value to `List` afterward is still unchecked and doesn't iterate over or validate any element.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- `read(List)` and `read(List)` have the same erased signature and can't coexist.
  Why: Changing only the return type doesn't resolve the name clash.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- A raw type assignment or unchecked cast turns off part of the compiler's proof.
  Why: The resulting heap pollution often doesn't fail at the write; it fails on a later read where the compiler inserted a cast.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- Java rejects `new List[10]` because arrays check their runtime component type on writes while `List` isn't reifiable.
  Why: Generic varargs pass through an array transformation and can expose the same route to heap pollution.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- An erased method has no `T.class` from a particular call, and a type parameter's bound doesn't describe an invocable constructor, so `new T()` is illegal too.
  Why: Replacing it with `(T) new Object()` merely converts a compile error into a runtime failure.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- `getDeclaredMethods()` can return both the source method and a compiler-generated bridge.
  Why: Registering callbacks by method name or annotation alone can create duplicate routes and can make behavior depend on unspecified method-array order.
  Source: [Type erasure](https://codewiki.com/java/type-erasure/)
- Do not assume this is safe: calling one list permanently a "producer" or "consumer" picks the wrong bound as soon as a method changes the data flow.
  Why: The same `List` can produce numbers in an aggregation method and consume integers in a fill method.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
- Generated copy methods often declare the destination as `List` and the source as `List`.
  Why: The destination then can't accept `T`, while the source can produce only `Object`; both ends lose the capability the implementation needs.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
- `List` prevents adding a non-null element through that reference, but it doesn't make the underlying list immutable.
  Why: `clear()`, indexed removal, iterator removal, and some reorder operations may still succeed.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
- Two `List` parameters could refer to `List` and `List`.
  Why: Both can be read as `Number`, but their elements cannot therefore be exchanged. A helper can't manufacture proof that two unknown types are equal.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
- Returning `List` leaves the caller with only an upper-bounded view.
  Why: Downstream code often has to propagate the wildcard or add a cast. The return looks flexible but may throw away a relationship the implementation could have promised.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
- After seeing a `CAP#1` diagnostic, a model may replace the parameter with raw `List`, add a `(T)` cast, and silence it with `@SuppressWarnings("unchecked")`.
  Why: That merely postpones a relationship error until a distant `ClassCastException`.
  Source: [Wildcards and PECS](https://codewiki.com/java/wildcards-pecs/)
