Types constrain values; contracts give them meaning
A service receives retries as text, converts it to int and invokes an operation. These are three separate decisions. The text must be parseable, the value must fit the type, and the service rule may allow only 0 through 5 attempts. Integer -2 fits the int representation while still violating that contract. Do not automatically turn invalid input into absence: a missing setting may allow a default, whereas an explicitly invalid setting may prevent startup.
Primitive types store values, including int, long, double and boolean. An Integer variable holds a wrapper reference and may be null. Use this distinction when reading an API signature: int cannot represent absence through null; Integer permits it, but consuming code must decide its meaning. A type declaration does not replace range, unit or required-value rules.
Initialization and unboxing
Fields and array elements receive default initial values: zero for int, false for boolean and null for references. A local variable declared inside a method requires assignment before it is read. Thus int retries; followed by use(retries); fails at compilation. It does not depend on old memory contents.
With Integer limit = null, the declaration is valid. Expression limit > 0 needs the wrapper’s int value, an operation called unboxing, and throws NullPointerException. The failure occurs at numeric use. If null must be rejected, limit != null && limit > 0 protects that use: && evaluates its right operand only when the left is true. Boolean & evaluates both sides; putting the comparison before the guard also loses protection. Apply a default only when the contract permits it.
Operation type comes before destination type
Consider int accepted = 7; int total = 10; double ratio = accepted / total; the / operands are integers: the quotient is 0 and becomes 0.0 only afterwards. For a fractional proportion, ((double) accepted) / total widens one operand before division. You must also define the total-equals-zero case; changing type does not define a policy for an absent population.
The same order matters in products. Two int values of 50,000 have a mathematical product of 2,500,000,000, which does not fit int. long size = rows * width still computes an int product before assignment. (long) rows * width computes it as long; (long) (rows * width) converts too late. If the output must remain int, Math.multiplyExact(a, b), with int arguments, signals overflow through ArithmeticException. An unchecked cast can lose information; accepting only positive products also does not prove absence of overflow.
Precision and boundary values
A long identifier is not an approximate measurement. Passing it through double can lose precision even without exceeding double’s magnitude range. A later cast cannot recover lost bits. In an integration requiring exact identity, preserve the integer or a textual representation defined by the contract.
Integer division truncates toward zero: -7 / 2 is -3. Integer division by zero throws ArithmeticException. A different boundary occurs at Integer.MIN_VALUE / -1: its mathematical quotient is outside int and the operation returns MIN_VALUE without throwing. A promise to detect overflow requires explicitly handling that case.
For double measurements, NaN does not behave as an ordered number. A rule rejecting x < 0.0 || x > 100.0 and accepting everything else admits NaN. Positive rule x >= 0.0 && x <= 100.0 rejects it, or Double.isNaN(x) can be included in rejection. x == Double.NaN does not detect it. Units and bounds remain domain decisions.
Guided application: startup configuration
A fictional service reads timeoutSeconds. A missing key means 30 seconds; a present value must be an integer from 1 through 120. Compare four inputs: absence, "45", "later" and "-1". First decide whether an input exists. Only absence receives 30. For the others, Integer.parseInt obtains an int or signals unparseable text. Parsing "45" allows progress; "later" fails parsing; "-1" parses but fails the range check.
Do not use one catch that converts all errors to zero. That would erase the difference between a configuration-file mistake and a deliberate timeout choice. The diagnostic should identify the setting and error class without copying secrets from other settings. Before accepting the change, also compare boundaries 1 and 120 and neighbors 0 and 121. The assessment asks you to select the policy and interpret the described outcomes, without executing code.
Decision: validate before allocating capacity
An importer receives rows and width, both nonnegative int values bounded at 100,000. The contract permits at most 1,000,000 positions. The maximum input product fits long. Compute the product by widening an operand, compare it with the service limit, then cast to int and allocate. Even a type capable of representing the product does not authorize an allocation beyond the limit.
When reviewing a proposal, follow input value → operand type → computed result → validation → final conversion → effect. Changing only the destination variable leaves an earlier error intact. Allocating first and checking afterwards also uses information that may already be corrupted. Apply this reasoning to batch sizes, pagination limits and capacity estimates, keeping service units and limits explicit.
static int timeoutSeconds(String raw) {
if (raw == null) return 30;
int seconds = Integer.parseInt(raw);
if (seconds < 1 || seconds > 120) {
throw new IllegalArgumentException("timeoutSeconds outside range");
}
return seconds;
}Read the snippet as a method inside a class. It assumes null represents a missing setting. "45" returns 45; "later" produces NumberFormatException; "-1" reaches range rejection. Startup diagnostics can distinguish the two failure types.
Common pitfalls
Confusing wrappers with primitives; assuming local variables default to zero; casting after an operation; treating parseInt as domain validation; using == to detect NaN.
Related topics: References, identity and text · Methods, contracts and control flow · Decimals, time and boundaries
First define presence, units and bounds. Then establish each operation’s type and validate the result before producing effects.
Reference: JLS 25: Types, Values, and Variables · Java SE 25; no preview features