CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-dagger--dagger

A fast dependency injector for Java and Android that generates plain Java source code without using reflection or runtime bytecode generation

Pending
Overview
Eval results
Files

utility-types.mddocs/

Utility Types

Dagger provides several utility types and interfaces that enhance the dependency injection experience, offering lazy evaluation, members injection, and other advanced features.

Capabilities

Lazy<T> Interface

Provides a handle to a lazily-computed value that is computed on the first call to get() and remembered for subsequent calls. Each Lazy<T> instance works independently.

/**
 * Handle to lazily-computed value - computes on first get(), remembers result
 */
interface Lazy<T> {
    /**
     * Returns the underlying value, computing it if necessary
     * @return the lazily-computed value
     */
    T get();
}

Key Features:

  • Thread-safe: value computed at most once even with concurrent access
  • Each Lazy<T> instance is independent (not singleton behavior)
  • Different from @Singleton which shares the same instance across all clients
  • Useful for expensive-to-create objects that may not be needed

Usage Examples:

public class ExpensiveServiceClient {
    private final Lazy<ExpensiveService> expensiveService;
    private final RegularService regularService;
    
    @Inject
    public ExpensiveServiceClient(
        Lazy<ExpensiveService> expensiveService,
        RegularService regularService
    ) {
        this.expensiveService = expensiveService;
        this.regularService = regularService;
    }
    
    public void performOperation() {
        // RegularService created immediately during injection
        regularService.doSomething();
        
        // ExpensiveService only created when first accessed
        if (shouldUseExpensiveFeature()) {
            expensiveService.get().performExpensiveOperation();
        }
    }
}

// Module providing lazy dependency
@Module
public class ServiceModule {
    @Provides
    @Singleton
    ExpensiveService provideExpensiveService() {
        // This won't be called until Lazy.get() is invoked
        return new ExpensiveService();
    }
}

MembersInjector<T> Interface

Injects dependencies into the fields and methods of existing instances. Used when Dagger cannot create the object directly but needs to inject dependencies into it.

/**
 * Injects dependencies into fields and methods of instances
 */
interface MembersInjector<T> {
    /**
     * Injects dependencies into the given instance
     * @param instance the instance to inject dependencies into
     */
    void injectMembers(T instance);
}

Key Features:

  • Injects @Inject-annotated fields and methods
  • Does not perform constructor injection (object must already exist)
  • Components automatically perform members injection after constructor injection
  • Useful for framework integration where object creation is external

Usage Examples:

// Class with members injection
public class ExternallyCreatedActivity {
    @Inject UserService userService;
    @Inject DatabaseService databaseService;
    
    private String activityData;
    
    // Constructor not managed by Dagger
    public ExternallyCreatedActivity(String data) {
        this.activityData = data;
    }
    
    @Inject
    void init(ConfigService configService) {
        // Method injection - called after field injection
        configure(configService.getActivityConfig());
    }
}

// Component with members injection
@Component(modules = ServiceModule.class)
public interface ActivityComponent {
    // Members injection method
    void inject(ExternallyCreatedActivity activity);
    
    // Alternative: return injected instance  
    ExternallyCreatedActivity inject(ExternallyCreatedActivity activity);
    
    // Direct MembersInjector access
    MembersInjector<ExternallyCreatedActivity> activityInjector();
}

// Usage
public class ActivityManager {
    private final ActivityComponent component;
    
    @Inject
    public ActivityManager(ActivityComponent component) {
        this.component = component;
    }
    
    public void setupActivity(String data) {
        // Create instance externally
        ExternallyCreatedActivity activity = new ExternallyCreatedActivity(data);
        
        // Inject dependencies
        component.inject(activity);
        
        // Alternative using MembersInjector directly
        MembersInjector<ExternallyCreatedActivity> injector = 
            component.activityInjector();
        injector.injectMembers(activity);
    }
}

Provider<T> Interface (JSR-330)

Standard JSR-330 interface for providing instances of a type. Every dependency in Dagger can be injected as Provider<T> to get new instances on each call.

/**
 * JSR-330 interface for providing instances of a type
 */
interface Provider<T> {
    /**
     * Provides an instance of type T
     * @return an instance of T
     */
    T get();
}

Key Features:

  • Part of javax.inject standard
  • get() may return different instances depending on scope
  • Allows deferred/repeated instance creation
  • Useful for factory patterns and optional dependencies

Usage Examples:

public class UserController {
    private final Provider<UserSession> sessionProvider;
    private final UserService userService;
    
    @Inject
    public UserController(
        Provider<UserSession> sessionProvider,
        UserService userService
    ) {
        this.sessionProvider = sessionProvider;
        this.userService = userService;
    }
    
    public void handleRequest() {
        // Get fresh session for each request
        UserSession session = sessionProvider.get();
        userService.processRequest(session);
    }
}

// Scoped vs Unscoped behavior
@Module
public class SessionModule {
    // Unscoped: Provider.get() returns new instance each time
    @Provides
    UserSession provideUserSession() {
        return new UserSession();
    }
    
    // Scoped: Provider.get() returns same instance within scope
    @Provides
    @Singleton
    UserPreferences provideUserPreferences() {
        return new UserPreferences();
    }
}

Optional Injection

Dagger supports Optional<T> injection for dependencies that may or may not be present:

public class FeatureService {
    private final CoreService coreService;
    private final Optional<AnalyticsService> analyticsService;
    
    @Inject
    public FeatureService(
        CoreService coreService,
        Optional<AnalyticsService> analyticsService
    ) {
        this.coreService = coreService;
        this.analyticsService = analyticsService;
    }
    
    public void performFeature() {
        coreService.execute();
        
        // Analytics may or may not be available
        analyticsService.ifPresent(analytics -> 
            analytics.trackFeatureUsage("feature_performed"));
    }
}

// Module declaring optional binding
@Module
public abstract class OptionalModule {
    @BindsOptionalOf
    abstract Optional<AnalyticsService> optionalAnalytics();
}

Lazy Provider Combinations

Dagger supports combinations of lazy evaluation and provider patterns:

public class ComplexDependencyClient {
    private final Lazy<Provider<ExpensiveService>> lazyProvider;
    private final Provider<Lazy<AnotherService>> providerLazy;
    
    @Inject
    public ComplexDependencyClient(
        Lazy<Provider<ExpensiveService>> lazyProvider,
        Provider<Lazy<AnotherService>> providerLazy
    ) {
        this.lazyProvider = lazyProvider;
        this.providerLazy = providerLazy;
    }
    
    public void complexOperation() {
        // Lazy<Provider<T>>: Lazy evaluation of provider itself
        Provider<ExpensiveService> provider = lazyProvider.get();
        ExpensiveService service1 = provider.get();
        ExpensiveService service2 = provider.get(); // May be different instance
        
        // Provider<Lazy<T>>: Provider of lazy instances
        Lazy<AnotherService> lazy1 = providerLazy.get();
        Lazy<AnotherService> lazy2 = providerLazy.get(); // Different lazy instances
    }
}

Qualifier Integration

All utility types work with qualifiers:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Background {}

public class QualifiedUtilityExample {
    @Inject
    public QualifiedUtilityExample(
        @Background Lazy<ExecutorService> backgroundExecutor,
        @Background Provider<WorkerThread> backgroundWorkers,
        @Background Optional<CacheService> backgroundCache
    ) {
        // All utilities respect qualifiers
    }
}

Utility Types Best Practices

Use Lazy for Expensive Objects:

// Good: Lazy evaluation for expensive creation
@Inject Lazy<DatabaseConnection> dbConnection;

// Avoid: Immediate creation if not always needed
@Inject DatabaseConnection dbConnection;

Provider for Multiple Instances:

// Good: When you need fresh instances
@Inject Provider<UserSession> sessionProvider;

// Avoid: Provider when singleton behavior is desired
@Inject Provider<ApplicationConfig> configProvider; // Use direct injection

MembersInjector for Framework Integration:

// Good: Framework objects Dagger can't create
public void onCreate(Bundle savedInstanceState) {
    component.inject(this); // Android Activity
}

// Avoid: MembersInjector for regular objects
// Use constructor injection instead

Optional for Truly Optional Dependencies:

// Good: Feature may not be available
@Inject Optional<AnalyticsService> analytics;

// Avoid: Required dependencies as Optional
@Inject Optional<DatabaseService> database; // Should be required

Install with Tessl CLI

npx tessl i tessl/maven-com-google-dagger--dagger

docs

assisted-injection.md

component-framework.md

index.md

module-system.md

multibindings.md

utility-types.md

tile.json