liveBook · Manning

Side effect

As explained in 100 Java Mistakes and How to Avoid Them

A side effect is an observable state change or external interaction caused by evaluating an operation in addition to producing its direct result.

A side effect is behavior beyond an operation’s direct result: changing program state or performing work whose occurrence, order, or repetition matters. Keep result computation separate from such work where possible; the computation is then easier to test and reuse. section 3.1 section 9.2

Side effects make control flow significant. A malformed conditional chain can overwrite selected data before a final process(data) call. Computing data in one method and invoking process separately isolates the effect; using final locals can expose unintended reassignment. section 3.1

Avoid effects in Stream intermediate operations

Functions supplied to map() and filter() should normally derive their outcome only from return values. Adding rejected elements to another list inside filter() depends on exactly-once, ordered evaluation and can fail or lose data with parallelStream(). Use partitioningBy() when the goal is to split elements into two groups. section 9.2

List<String> containNoNeedle = new ArrayList<>();
List<String> containNeedle = list.stream()
  .filter(str -> {
    if (str.contains("needle")) {
      return true;
    }
    containNoNeedle.add(str);
    return false;
  })
  .toList();

This filter both classifies strings and mutates a second collection. The collector-based alternative produces both partitions without relying on intermediate-operation side effects. section 9.2

Do not place required work inside assert. Assertions may be disabled, so their expressions are not evaluated: set.add(value) may never run, and Matcher.find() may not populate groups needed afterward. Evaluate the operation first, then assert its result; use an explicit if for a failed precondition. section 10.1 section 10.8

Conditions should also avoid incidental updates. Non-short-circuit & and | evaluate both operands, which can intentionally run two update methods but obscures that intent and may be mistaken for a typo. Call the updates in separate statements, retain their boolean results, then combine those results with &&. section 2.7

How 100 Java Mistakes and How to Avoid Them covers it

How deep it goes
Explains consequences and refactorings for effects in conditionals, Streams, assertions, and boolean operators without defining runtime internals.
Examples
Uses conditional data selection, Stream partitioning, set insertion, Matcher.find(), and paired update methods.
What it assumes
Assumes Java familiarity with conditionals, methods, collections, and lambda-based Stream pipelines.
What you can do after
Apply it by isolating required mutations, choosing collectors, and moving assertion or condition effects into explicit statements.
How it connects
Connects side effects with Stream API, assertions, short-circuit operators, static analysis, and unit tests.

Also explained in

Compare how 24 books explain side effect →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Java Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.