TL;DR
Prefer interface types in your APIs and variables for flexibility and testability. Use a concrete class only when no suitable interface exists (e.g., BigInteger). Don’t hide needed features behind too-narrow interfaces.
🔸 GOOD USE / BAD USE
▪️ ✅ Good: List names = new ArrayList<>(); (code depends on List, not the implementation)
▪️ ✅ Good (APIs): void saveAll(Collection items)
▪️ ❌ Bad: ArrayList names = new ArrayList<>(); (locks you in)
▪️ ❌ Bad (APIs): ArrayList saveAll(ArrayList items)
🔸 FLEXIBILITY
▪️ Swap implementations without touching callers (ArrayList ↔ LinkedList, HashMap ↔ LinkedHashMap) 🔄
▪️ Easier testing/mocking (Map, List, Queue) 🧪
▪️ Clearer intent: what you need, not how it’s done 🧠
🔸 CAVEAT: SPECIAL FUNCTIONALITY NOT IN THE INTERFACE
▪️ If you need methods beyond the interface (e.g., ArrayDeque’s specifics), either:
▪️ Use the right interface (Deque vs Queue)
▪️ Accept the concrete type in that scope
▪️ Downcasting to reach missing methods is a smell 🚨
🔸 IF NO APPROPRIATE INTERFACE, USE THE CLASS (E.G., BigInteger)
▪️ Some types are inherently concrete: String, BigInteger, BigDecimal, Path
▪️ In such cases, using the class is the clean choice 👍
🔸 WHEN NO INTERFACE, THAT’S IT!
▪️ Don’t invent fake abstractions 🧱
▪️ Keep it simple and explicit with the concrete type
🔸 TAKEAWAYS
▪️ Default to interfaces for APIs, fields, and variables
▪️ Choose the narrowest interface that exposes what you need
▪️ Use concrete classes when there’s no suitable interface or when you need implementation-specific features
#java #cleancode #oop #solid #effectivejava #spring #javadeveloper #softwaredesign #bestpractices
Go further with Java certification:
Java👇
Spring👇
SpringBook👇