Files

4.5 KiB

Guice Conversion Guide

When This Applies

This guide applies when the Java source contains imports matching com.google.inject.*. This covers Google Guice core, Guice multibindings, and Guice servlet.

Key Rules

1. @Inject constructor syntax

Kotlin places @Inject before the constructor keyword in the primary constructor:

class Foo @Inject constructor(private val bar: Bar)

2. @Provides methods in Modules

Keep @Provides methods as regular functions. Guice modules extend AbstractModule, so override configure() as usual.

3. Module.configure() override

Override configure() in Kotlin. Use Guice's binding DSL with Kotlin class references:

bind(Foo::class.java).to(FooImpl::class.java)

4. @Named qualifier — annotation site targets

In Kotlin, @Named on constructor parameters needs a site target to reach the parameter (not the field or property). Use @param:Named for constructor injection:

class Foo @Inject constructor(
    @param:Named("primary") private val dataSource: DataSource
)

When used on function parameters (e.g., in @Provides methods), no site target is needed.

5. @Singleton scope

Preserve @Singleton exactly. It can be placed on the class declaration or in module bindings via .in(Singleton::class.java).

6. Provider

Provider<T> can stay as-is for lazy or scoped injection. Where the only purpose is deferred initialization, Kotlin's lazy delegation can be used as an alternative outside of Guice-managed contexts.


Examples

Example 1: Guice Module with Bindings and an Injected Class

Java:

package com.acme.config;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;

public class AppModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(CacheService.class).to(RedisCacheService.class);
        bind(NotificationService.class).to(EmailNotificationService.class).in(Singleton.class);
    }

    @Provides
    @Singleton
    public HttpClient provideHttpClient(@Named("baseUrl") String baseUrl) {
        return new HttpClient(baseUrl);
    }
}
package com.acme.service;

import com.google.inject.Inject;
import com.google.inject.name.Named;

public class OrderService {

    private final CacheService cacheService;
    private final HttpClient httpClient;
    private final String region;

    @Inject
    public OrderService(CacheService cacheService, HttpClient httpClient, @Named("region") String region) {
        this.cacheService = cacheService;
        this.httpClient = httpClient;
        this.region = region;
    }

    public Order findById(Long id) {
        return cacheService.getOrFetch(id, () -> httpClient.get("/orders/" + id, Order.class));
    }
}

Kotlin:

package com.acme.config

import com.google.inject.AbstractModule
import com.google.inject.Provides
import com.google.inject.Singleton
import com.google.inject.name.Named

class AppModule : AbstractModule() {

    override fun configure() {
        bind(CacheService::class.java).to(RedisCacheService::class.java)
        bind(NotificationService::class.java).to(EmailNotificationService::class.java).`in`(Singleton::class.java)
    }

    @Provides
    @Singleton
    fun provideHttpClient(@Named("baseUrl") baseUrl: String): HttpClient {
        return HttpClient(baseUrl)
    }
}
package com.acme.service

import com.google.inject.Inject
import com.google.inject.name.Named

class OrderService @Inject constructor(
    private val cacheService: CacheService,
    private val httpClient: HttpClient,
    @param:Named("region") private val region: String
) {

    fun findById(id: Long): Order? {
        return cacheService.getOrFetch(id) { httpClient.get("/orders/$id", Order::class.java) }
    }
}

Key changes:

  • @Inject moves before the constructor keyword in the primary constructor.
  • Constructor parameters become private val in the primary constructor.
  • @Named("region") uses @param:Named site target so the annotation reaches the constructor parameter rather than the Kotlin property.
  • .in(Singleton.class) becomes .`in`(Singleton::class.java)in is a reserved keyword in Kotlin and must be escaped with backticks.
  • The lambda in getOrFetch uses Kotlin's trailing lambda syntax instead of an anonymous inner class.
  • String concatenation "/orders/" + id becomes a string template "/orders/$id".