# How to Implement the Dependency Injection Pattern for Loose Coupling in Java

> Learn how to implement the Dependency Injection pattern in Java to achieve loose coupling. Swap implementations easily without modifying existing code and improve maintainability.

- Repository: [Ilkka Seppälä/java-design-patterns](https://github.com/iluwatar/java-design-patterns)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The Dependency Injection pattern eliminates tight coupling by delegating dependency creation to external sources, enabling you to swap implementations without modifying class internals.**

The **Dependency Injection (DI)** pattern is a cornerstone of maintainable Java applications, promoting loose coupling by ensuring classes depend on abstractions rather than concrete implementations. In the `iluwatar/java-design-patterns` repository, this pattern is demonstrated through a wizard example that evolves from rigid, tightly-coupled code to a flexible, framework-managed solution using Google Guice.

## Understanding Dependency Injection and Inversion of Control

**Inversion of Control (IoC)** is the fundamental principle behind Dependency Injection. Instead of high-level modules (the wizards) instantiating low-level modules (specific tobacco types), both depend on a shared abstraction. As documented in [`App.java`](https://github.com/iluwatar/java-design-patterns/blob/main/App.java) at lines 30-34, this shift ensures that neither the wizard nor the specific tobacco implementation holds control over object creation. The `Wizard` interface and the `Tobacco` abstraction form the contract that decouples the components, allowing you to substitute `SecondBreakfastTobacco`, `RivendellTobacco`, or `OldTobyTobacco` without altering wizard logic.

## Constructor Injection for Mandatory Dependencies

**Constructor injection** is the preferred method for providing required dependencies, guaranteeing that an object is created in a complete, valid state. In [`AdvancedWizard.java`](https://github.com/iluwatar/java-design-patterns/blob/main/AdvancedWizard.java), the wizard declares its dependency on the `Tobacco` interface through the constructor parameter, removing any responsibility for creating the concrete instance.

```java
// Constructor injection – the wizard declares a dependency but does not create it.
public class AdvancedWizard implements Wizard {
  private final Tobacco tobacco;      // depends on abstraction
  public AdvancedWizard(Tobacco tobacco) {
    this.tobacco = tobacco;          // injected by client code
  }
  public void smoke() {
    tobacco.smoke(this);
  }
}

```

Because the `tobacco` field is declared as `final`, the dependency cannot be altered after construction, ensuring immutability and thread safety. The client code that instantiates `AdvancedWizard` maintains control over which specific tobacco implementation to provide.

## Setter Injection for Flexible Configuration

**Setter injection** offers greater flexibility by allowing dependencies to be swapped after object creation. The [`AdvancedSorceress.java`](https://github.com/iluwatar/java-design-patterns/blob/main/AdvancedSorceress.java) implementation demonstrates this approach, exposing a setter method that modifies the internal state.

```java
// Setter injection – the dependency can be swapped later.
public class AdvancedSorceress implements Wizard {
  private Tobacco tobacco;           // mutable dependency
  public void setTobacco(Tobacco tobacco) {
    this.tobacco = tobacco;
  }
  public void smoke() {
    tobacco.smoke(this);
  }
}

```

This pattern suits optional dependencies or scenarios requiring runtime reconfiguration. However, you must guard against null dependencies, as the object can exist temporarily without its dependency being set.

## Framework-Based Injection with Google Guice

Modern Java applications often delegate wiring entirely to a **Dependency Injection framework** like Google Guice. The `iluwatar/java-design-patterns` repository illustrates this through [`TobaccoModule.java`](https://github.com/iluwatar/java-design-patterns/blob/main/TobaccoModule.java) and [`GuiceWizard.java`](https://github.com/iluwatar/java-design-patterns/blob/main/GuiceWizard.java), eliminating manual object construction.

The `TobaccoModule` extends Guice's `AbstractModule` to declaratively bind the `Tobacco` abstraction to a concrete implementation:

```java
// Guice configuration – binding an abstraction to a concrete class.
public class TobaccoModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(Tobacco.class).to(SecondBreakfastTobacco.class);
  }
}

```

The `GuiceWizard` receives its dependencies automatically through the framework's `Injector`:

```java
// Obtaining an instance via Guice – no manual wiring needed.
Injector injector = Guice.createInjector(new TobaccoModule());
GuiceWizard wizard = injector.getInstance(GuiceWizard.class);
wizard.smoke();

```

This approach removes factory boilerplate and centralizes configuration in the module class, making the system easier to modify and test.

## Summary

- **Dependency Injection** delegates object creation to external sources, preventing classes from instantiating their own dependencies.
- **Constructor injection** ensures mandatory dependencies are provided at initialization and supports immutability.
- **Setter injection** allows runtime flexibility for optional dependencies that may change during the object's lifecycle.
- **Frameworks like Google Guice** automate the binding of abstractions to concrete implementations via configuration modules such as [`TobaccoModule.java`](https://github.com/iluwatar/java-design-patterns/blob/main/TobaccoModule.java).
- Depending on the `Tobacco` interface rather than concrete implementations enables swapping between `SecondBreakfastTobacco`, `RivendellTobacco`, or `OldTobyTobacco` without modifying wizard classes.

## Frequently Asked Questions

### What is the difference between Dependency Injection and Inversion of Control?

**Inversion of Control** is the architectural principle where control of object creation shifts from the class itself to an external container or framework. **Dependency Injection** is the specific implementation technique where dependencies are supplied to a class from an external source rather than the class creating them internally. According to the source code in [`App.java`](https://github.com/iluwatar/java-design-patterns/blob/main/App.java), IoC ensures that both high-level wizards and low-level tobacco implementations depend on the abstraction rather than concrete instances.

### When should I use constructor injection versus setter injection?

Use **constructor injection** for mandatory dependencies that the object requires to function correctly, ensuring the object is always in a valid state upon creation as demonstrated in [`AdvancedWizard.java`](https://github.com/iluwatar/java-design-patterns/blob/main/AdvancedWizard.java). Use **setter injection** for optional dependencies that may change during the object's lifecycle or when you need to allow post-construction configuration, as shown in [`AdvancedSorceress.java`](https://github.com/iluwatar/java-design-patterns/blob/main/AdvancedSorceress.java).

### How does Google Guice improve upon manual Dependency Injection?

Google Guice automates the wiring process through binding modules like [`TobaccoModule.java`](https://github.com/iluwatar/java-design-patterns/blob/main/TobaccoModule.java), eliminating the need for factories or manual object construction in client code. The Guice `Injector` automatically resolves dependency graphs and instantiates objects such as `GuiceWizard` with their required dependencies already populated, reducing boilerplate while maintaining strict loose coupling.

### Why is depending on abstractions important for loose coupling?

Depending on the `Tobacco` interface rather than concrete classes like `SecondBreakfastTobacco` allows you to swap implementations without modifying the wizard classes that consume them. This adherence to the Dependency Inversion Principle enables testing with mock implementations and switching production implementations based on configuration, which is the primary goal of the Dependency Injection pattern.