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
Listoperations"); -
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.