What are the Types of Dependency Injection?
Constructor, setter and interface injection compared, with Java examples and interview-ready explanations of when to use each type.
Expected Interview Answer
There are three main types of dependency injection: constructor injection (dependencies passed into the constructor), setter injection (dependencies assigned through public setter methods after construction), and interface/method injection (dependencies passed through a method defined by an injector interface).
Constructor injection is the most widely recommended form because it lets an object declare its required dependencies up front and become immutable and fully valid the moment it is constructed. Setter injection is useful for optional dependencies that can have sensible defaults or be reconfigured after creation, but it allows an object to briefly exist in a partially initialized state. Interface injection is the least common in modern frameworks; it defines an explicit injector interface with an “inject” method that the container calls, giving the framework more control but adding boilerplate. Field injection, sometimes mentioned as a fourth style, directly sets private fields via reflection, which is convenient but hides dependencies and hurts testability, so most teams avoid it.
- Constructor injection guarantees immutability and mandatory dependencies
- Setter injection supports optional or reconfigurable dependencies
- Interface injection gives frameworks explicit control over wiring
- Choosing the right type improves testability and object validity
AI Mentor Explanation
A player arriving at the ground with bat, pads and helmet already packed in the kit bag is constructor injection: everything mandatory is supplied at the start, and the player is match-ready the moment they walk in. A player who forgets sunglasses and has the twelfth man hand them over mid-innings is setter injection: an optional item supplied after the fact. A coach who insists all gear must be handed through an official kit-check officer, following a fixed checklist interface, mirrors interface injection: a defined channel the supplier must use.
Step-by-Step Explanation
Step 1
Identify mandatory dependencies
Anything the object cannot function without belongs in the constructor.
Step 2
Use constructor injection for required dependencies
Pass them as constructor parameters so the object is valid the moment it exists.
Step 3
Use setter injection for optional dependencies
Expose a public setter for dependencies with sensible defaults or that may change later.
Step 4
Reserve interface injection for framework-controlled wiring
Implement an injector interface only when the container needs an explicit contract to call.
What Interviewer Expects
- Correct definitions of constructor, setter, and interface injection
- A clear statement of why constructor injection is generally preferred
- Awareness of field injection as an anti-pattern for testability
- A concrete Java example distinguishing at least two of the types
Common Mistakes
- Treating dependency injection as only “annotations” without knowing the underlying mechanism
- Using setter injection for mandatory dependencies, allowing invalid partial states
- Confusing interface injection with simply implementing any interface
- Not knowing field injection via reflection hurts testability and hides dependencies
Best Answer (HR Friendly)
“There are a few ways to hand an object the things it depends on. Constructor injection passes them in when the object is created, so it is always fully ready to use. Setter injection lets you supply optional dependencies after creation through a method call. Interface injection is a less common approach where the framework calls a defined method to hand over dependencies. In practice, most teams default to constructor injection because it keeps objects valid and easy to test.”
Code Example
interface MessageInjector {
void injectMessageService(MessageService service);
}
class OrderProcessor implements MessageInjector {
private final PaymentGateway gateway; // mandatory
private NotificationService notifier; // optional
private MessageService messageService; // injected via interface
// Constructor injection: required dependency
OrderProcessor(PaymentGateway gateway) {
this.gateway = gateway;
}
// Setter injection: optional dependency
void setNotifier(NotificationService notifier) {
this.notifier = notifier;
}
// Interface injection: container calls this explicitly
@Override
public void injectMessageService(MessageService service) {
this.messageService = service;
}
}Follow-up Questions
- Why is constructor injection generally preferred over setter injection?
- What problems does field injection cause in unit testing?
- When would setter injection be the right choice?
- How does interface injection differ from constructor injection in practice?
MCQ Practice
1. Which type of dependency injection best guarantees an object is fully valid immediately after creation?
Constructor injection supplies all required dependencies at construction time, so the object is never in an incomplete state.
2. Setter injection is most appropriate for?
Setter injection suits dependencies that are optional or may be changed after the object is constructed.
3. A key drawback of field injection via reflection is?
Field injection sets private fields directly, making dependencies invisible in the public API and harder to mock in tests.
Flash Cards
Three main types of dependency injection? — Constructor injection, setter injection, and interface injection.
Which type is generally preferred and why? — Constructor injection, because it guarantees mandatory dependencies exist and the object is immutable and valid at creation.
When is setter injection appropriate? — For optional dependencies with sensible defaults that may be reconfigured later.
What does interface injection require? — The dependent class implements an injector interface with a method the container calls to supply the dependency.