Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java checked integer arithmetic: reject overflow before updating state

Last updated: 1 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

Math.addExact computes an int or long sum and throws ArithmeticException instead of silently wrapping an out-of-range result.

Calculate before publishing

A counter near Integer.MAX_VALUE can wrap negative with ordinary addition. In inventory or billing state, that is not a harmless numerical oddity: later bounds checks may accept a wrong value. Compute the candidate with addExact first, then assign it to the live counter. The program reaches the int maximum exactly and rejects one more unit without changing the stored count.

A wider long delays overflow but does not remove it. For unbounded mathematical totals, choose BigInteger; for decimal amounts, use an explicit BigDecimal policy. Numeric promotion matters before the checked call: an overflowing expression inside an argument may already be wrong by the time addExact sees it.

Make failure part of the API

This small counter rejects a negative increment and exposes a read method. A real shared counter also needs synchronization or an atomic state transition; addExact alone does not make a read-modify-write thread-safe. Atomic variables and locks cover that separate problem.

Multiplication, subtraction, narrowing to int, and increment have corresponding exact methods. Use the operation matching the domain rule rather than testing after a wrapped value has escaped into another object.

Working program

Java
public class CheckedStockCounter {
    static final class StockCounter {
        private int received;
        StockCounter(int received) { this.received = received; }
        void add(int units) {
            if (units < 0) throw new IllegalArgumentException("negative units");
            int candidate = Math.addExact(received, units);
            received = candidate;
        }
        int received() { return received; }
    }
    public static void main(String[] args) {
        StockCounter counter = new StockCounter(2_147_483_600);
        counter.add(47);
        System.out.println(counter.received());
        try { counter.add(1); }
        catch (ArithmeticException rejected) { System.out.println("overflow rejected"); }
        System.out.println(counter.received());
    }
}

Output

Output
2147483647
overflow rejected
2147483647

Cost and ownership

Each checked primitive operation has O(1) time and storage cost. This example protects arithmetic correctness for one thread; a concurrent counter requires an additional atomicity boundary around the read, calculation, and write.

Common Mistakes

  • Do not check an int after it has already wrapped and call that overflow protection.
  • Do not assume long is mathematically unbounded.
  • Do not confuse exact arithmetic with concurrent atomic updates.

Read next

Java operators and numeric promotion, Java atomic variables: compare-and-set and one-variable invariants, Java ReentrantLock: protect a complete state transition, Java BigDecimal: decimal amounts and explicit rounding.

More numeric representation boundaries

Continue with Java Math.abs on Integer.MIN_VALUE: detect the one value that cannot turn positive, Java BigInteger.intValueExact: reject a narrowing conversion, Java long to double: large integer IDs can lose unit precision.

java
checked-integer-arithmetic
Storage details