Skip to main content

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.

java
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.

java
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.)

java
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:

java
interface DatabaseAware { void setDatabase(Database db); }

The container finds the interface implementation and calls setDatabase() itself.


Summary table

DI typeHow it is injectedWhen to useProsCons
ConstructorThrough the constructorRequired dependenciesSafety, testabilityMany parameters
SetterThrough methodsOptional dependenciesFlexibilityPossible incomplete initialization
FieldThrough fieldsFast setup, simple classesMinimal codeHidden dependency, hard to test
InterfaceThrough an interface methodSpecialized frameworksFlexibilityRarely 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.