Loose Coupling

ID

java.loose_coupling

Severity

info

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Best Practice

Language

Java

Tags

best-practice

Description

Reports fields, local variables and method parameters declared with a concrete collection type (for example ArrayList, HashMap, HashSet) rather than the corresponding interface (List, Map, Set).

Rationale

Declaring a variable or parameter with a concrete class ties the caller, the callee, and every subsequent reader to that specific implementation. If you later want to switch to a LinkedList, a ConcurrentHashMap, or an immutable/unmodifiable view, every signature and every local along the call path has to change.

Declaring against the interface is the canonical way to express "I need a list / map / set; I don’t care how it’s stored":

  • it documents intent ("the callee uses only List operations");

  • it lets construction and the contract evolve independently;

  • it mirrors the advice every widely-used Java style guide gives (Effective Java Item 64 — "Refer to objects by their interfaces").

// Bad — caller is welded to ArrayList
private ArrayList<String> items = new ArrayList<>();

void handle(HashMap<String, Integer> counts) { ... }

Remediation

Use the collection interface in the declaration; keep the concrete class in the new expression (or factory call):

// Good — declaration talks to the interface
private List<String> items = new ArrayList<>();

void handle(Map<String, Integer> counts) { ... }

A var local is also fine when the intent is obvious from the initializer — the inferred type is a concrete one, but it stays local to the block, so the coupling doesn’t leak into signatures.