What types of dependency injection exist?
There are three main types of dependency injection - based on how an object receives its dependencies:
1. Constructor Injection
The most reliable and popular way. The dependency is passed when the object is created, through the constructor.
class UserService {
private final Database db;
public UserService(Database db) { // injection
this.db = db;
}
}Features:
- All required dependencies are set right away.
- The object cannot be created without them, so it is always in a valid state.
- Fits immutable classes very well.
- Simplifies testing: a mock can be plugged in at creation time.
Pros: safety, clarity, testability. Con: if there are many dependencies, the constructor becomes bulky.
2. Setter Injection
Dependencies are passed through public methods after the object is created.
class UserService {
private Database db;
public void setDatabase(Database db) { // injection
this.db = db;
}
}Features:
- The dependency can be changed during the object's lifetime.
- Used for optional dependencies.
Pros: flexibility, possibility of late binding. Cons: the object can be created in an "incomplete" state; more room for errors.
3. Field Injection
The container sets the dependency directly into a class field, bypassing the constructor. (Popular in Spring, Android, and others.)
class UserService {
@Autowired
private Database db; // injection
}Features:
- The shortest syntax: nothing needs to be written by hand.
- Fits rapid development and simple classes.
Pros: minimal code, simplicity. Cons:
- the dependency is "invisible" in the constructor (harder to test),
- the object is hard to create without the container,
- weak encapsulation.
4. (Sometimes listed as well)
Interface Injection - a rare option, where the container calls an interface method, passing the dependency in:
interface DatabaseAware {
void setDatabase(Database db);
}The container finds the interface implementation and calls setDatabase() itself.
Summary table
| DI type | How it is injected | When to use | Pros | Cons |
|---|---|---|---|---|
| Constructor | Through the constructor | Required dependencies | Safety, testability | Many parameters |
| Setter | Through methods | Optional dependencies | Flexibility | Possible incomplete initialization |
| Field | Through fields | Fast setup, simple classes | Minimal code | Hidden dependency, hard to test |
| Interface | Through an interface method | Specialized frameworks | Flexibility | Rarely used |
Conclusion:
In real projects, constructor injection (for required dependencies) and setter injection (for optional ones) are usually combined. Field injection is used where brevity matters, but not strict modularity.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.