danidin
danidin

Reputation: 381

How to Instantiate an object with dynamic members with Dagger2?

I have a class which gets arguments in a constructor:

public class Dependency{
  Dependency(int number1, int number2, int number3, DependencyListener listener){}     
}

each dependent class needs to path different arguments in order to instantiate a dependency. How should i define the module and the component that other classes could initiate it properly while transferring different values and passing 'this' as the listener?

Also, How should i use the @Inject method in this case?

Edit 1

@jeff-bowman I was thinking of using the following method:

@Module public class DependencyModule {
  int first;
  int second;
  int third;
  DependencyListener listener;

  public DependencyModule(int first, int second, int third, DependencyListener listener) {
    this.first = first;
    this.second = second;
    this.third = third;
    this.listener = listener
  }

  @Provides Dependency provideDependency(int first, int second, int third, DependencyListener listener) {
    return new Dependency(first, second, third, listener)
  }
}

@Component(modules = { DependencyModule.class }) public interface     
DependencyComponent {
  void inject(DependentClass1 target);
  void inject(DependentClass2 target);
  void inject(DependentClass3 target);
}

and in each DependentClass i would do:

public class DependentClass{
  @Inject Dependency;
  public DependentClass{
      DaggerDependencyComponent.builder().dependencyModule(new DependencyModule(first, second, third, this)).build().inject();
  }
}

Is it a good practice?

Upvotes: 1

Views: 1671

Answers (2)

khesam109
khesam109

Reputation: 620

To solve the problem, you can use Assisted Injection to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time. So, your Dependency class can be something like this (suppose that DependencyListener provided by the DI framework):

public record Dependency(int number, DependencyListener listener) {

    @AssistedInject
    public Dependency(@Assisted int number, DependencyListener listener) {
        this.number = number;
        this.listener = listener;
    }
}

In addition, you should define a factory that can be used to create an instance of the object and it should contain an abstract method that returns the @AssistedInject type and takes in all @Assisted parameters defined in its constructor like below:

@AssistedFactory
public interface DependencyFactory {
    Dependency buildDependency(int number);
}

Finally, you can inject this factory and use it to create the Dependency:

public class Dependent {

    private final DependencyFactory factory;

    @Inject
    public Dependent(DependencyFactory factory) {
        this.factory = factory;
    }

    Dependency getDependency(int number) {
        return factory.buildDependency(number);
    }
}

Upvotes: 0

Jeff Bowman
Jeff Bowman

Reputation: 95654

Conceptually, what you want is a Factory:

/** Inject this wherever you want an instance of Dependency. */
public interface DependencyFactory {
  Dependency create(
      int number1,
      int number2,
      int number3,
      DependencyListener listener);
}

public class DependencyFactoryImpl implements DependencyFactory {
  @Inject Provider<SomeDaggerDepIfNeeded> someDaggerDepProvider;

  @Override public void create(
      int number1,
      int number2,
      int number3,
      DependencyListener listener) {
    return new Dependency(number1, number2, number3, listener,
        someDaggerDepProvider.get() /* if necessary */);
  }
}

However, because this is so easy to generate automatically, there are often built-in tools to do so. Guice calls this Assisted Injection, and supplies FactoryModuleBuilder (via an optional extension JAR) that reflectively generates factories at runtime. Dagger 2 doesn't have a built-in equivalent, mostly because Google has already released an open-source JSR-330 factory generator called AutoFactory that generates a factory for any JSR-330 implementation including Dagger, Guice, and Spring.

@AutoFactory
public class Dependency{
  Dependency(
      int number1, int number2, int number3, DependencyListener listener,
      @Provided SomeDaggerDepIfNeeded somedaggerDep){}     
}

See the AutoFactory documentation for details on its calls, particularly if you're interested in having the generated factory implement an interface. Using an interface you define explicitly can make it easier to work with generated code in IDEs.

(If you don't have any need for dependencies from Dagger, or for substituting other implementations of this class to classes that depend on it, you can leave the class exactly as it is and treat the call to new as a factory.)

Upvotes: 1

Related Questions