How does EK9 DI compare to Google Guice modules?

← Dependency Injection · Ref: Q329

Google Guice uses Module classes with bind() statements to configure injection. EK9's 'defines application' with 'register' statements serves the same purpose but with compile-time validation.

GUICE MODULES VS EK9 APPLICATIONS

Guice:

  class AppModule extends AbstractModule {
    @Override void configure() {
      bind(Service.class).to(ServiceImpl.class);
    }
  }

EK9:

  defines application
    MyApp
      register ServiceImpl() as Service

KEY DIFFERENCES

1. VALIDATION TIMING: Guice validates at Injector creation (runtime). EK9 validates at compilation.
2. JUST-IN-TIME BINDINGS: Guice can create instances without explicit bindings (JIT). EK9 requires explicit registration for every injection point.
3. LINKED BINDINGS: Guice chains bind(A).to(B).to(C). EK9 registers concrete against abstract directly.
4. SCOPES: Guice has @Singleton, @RequestScoped, custom scopes. EK9 has singleton only.
5. PROVIDERS: Guice has Provider<T> for lazy/deferred creation. EK9 creates all components eagerly in prepare phase.

WHY NO JIT BINDINGS

Guice's JIT bindings mean a class with @Inject constructor can be injected without any bind() call. This is convenient but dangerous: it hides dependencies and makes the object graph unpredictable. EK9 requires every injection point to have an explicit registration.

INSTALLING MODULES

Guice composes modules: install(new DatabaseModule()). EK9 does not compose applications — each application is self-contained. If you need shared registrations, define shared components and register them in each application.

See Q227 for compile-time validation. See Q228 for registration ordering. See Q324 for injection syntax. See Q330 for scope comparison.

Example

defines module qa.di.guice.comparison

  defines component

    PaymentGateway as abstract
      charge() as abstract
        -> amount as String
        <- receipt as String?

      default operator ?

    StripeGateway is PaymentGateway
      override charge()
        -> amount as String
        <- receipt as String: "Stripe charged: " + amount

      default operator ?

    OrderProcessor as abstract
      process() as abstract
        -> orderId as String
        <- confirmation as String?

      default operator ?

    DefaultOrderProcessor is OrderProcessor
      gateway as PaymentGateway!

      override process()
        -> orderId as String
        <- confirmation <- String()
        confirmation: gateway.charge("100.00 for " + orderId)

      default operator ?

  defines application

    <?-
      This replaces Guice's AbstractModule.configure().
      register replaces bind().to().
    -?>
    GuiceEquivalentApp
      register StripeGateway() as PaymentGateway
      register DefaultOrderProcessor() as OrderProcessor

  defines program

    GuiceComparisonDemo() with application of GuiceEquivalentApp
      stdout <- Stdout()

      // === EXPLICIT REGISTRATION REPLACES bind().to() ===

      processor as OrderProcessor!

      result <- processor.process("ORD-001")
      stdout.println(result)

      stdout.println("Application registration replaces Guice modules")

Common mistakes

E08150 — Unlike Guice which supports JIT bindings to concrete types, EK9 requires injection fields to use abstract component types. Explicit registration maps concrete to abstract. See ek9 -h E08150 for details.

Incorrect:

gateway as StripeGateway!

Correct:

gateway as PaymentGateway!

E08210 — Removing the PaymentGateway registration leaves DefaultOrderProcessor's injection field unsatisfied. Unlike Guice JIT bindings, EK9 requires every injection point to have an explicit registration. See ek9 -h E08210 for details.

Incorrect:

register DefaultOrderProcessor() as OrderProcessor

Correct:

register StripeGateway() as PaymentGateway
      register DefaultOrderProcessor() as OrderProcessor
Other ways to ask this
  • What replaces Guice modules and bindings in EK9?
  • How is EK9 injection different from Google Guice?
  • Does EK9 have something like Guice's AbstractModule?

Coming from another language?

Google Guice: AbstractModule.configure() with bind().to() statements, @Provides methods, just-in-time bindings, Injector creation validates at runtime, Multibinder for collections, @Singleton scope. Dagger: compile-time DI for Android, @Component/@Module annotations. EK9: 'defines application' with 'register' statements, compile-time validation, no JIT bindings, explicit registration required, singleton scope only.

Keywords: inject, google, injector, bind, explicit, dagger, provider, guice, jit, dependency, migration, module