1

I'm trying to figure out how Guice works and is struggling to find examples/tutorials for the specific scenarios I have.

Lets say I have the following classes:

  • ClassA with injected ClassB
  • ClassB
  • ClassC with injected ClassA

The problem I'm having is that ClassA does not have a no-arg constructor and also have its own dependencies. I'm not sure how to inject these type of dependencies using an Injector.

ClassA

public class ClassA {

    private final int number;
    private final ClassB classB;

    @Inject
    public ClassA(int number, ClassB classB) {
        this.number = number;
        this.classB = classB;
    }

    public String message() {
        return "ClassA number: " + number + ", ClassB number: " + classB.getNumber();
    }

}

ClassB

@Getter
public class ClassB {

    private final int number;

    @Inject
    public ClassB(@Assisted int number) {
        this.number = number;
    }

}

ClassC

import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

public class ClassC {

    final private int number;
    final private ClassA classA;

    @Inject
    public ClassC(@Assisted int number, ClassA classA) {
        this.number = number;
        this.classA = classA;
    }

    public String message() {
        String message = classA.messsage();
        return message + ", ClassC number: " + number;
    }

}

I have a factory interfaces for all classes with a create(int number) method. In the module configure method I have the following:

install(new FactoryModuleBuilder().build(ClassAFactory.class));
install(new FactoryModuleBuilder().build(ClassBFactory.class));
install(new FactoryModuleBuilder().build(ClassCFactory.class));

The following is where I'm totally lost, how do I mix this all together?

Injector injector = Guice.createInjector(new AppModule());

ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);
ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1);
ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3);

System.out.println(c.message());

Output for above:

ClassA number: 3, ClassB number: 3, ClassC number: 3

Bonus for me, if its possible to use providers instead of factories for this scenario please can someone show me an example of how to resolve these dependencies in a provider?

1
  • This is working as expected. Each of those instances is their own thing. If you want to provide separate values, make sure each @Assisted value has a different name. There is no reason to assume the ClassB from your bFactory.getInstance call will be reused as the dependency of the aFactory.getInstance call. Commented Oct 11, 2023 at 9:18

1 Answer 1

1

This is actually a feature, believe it or not. It's creating corresponding instances with the same value.

ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);

This creates a new ClassB instance with number of 2.

ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1);

The constructor for ClassA takes number and an instance of ClassB. However, the instance of ClassB you've created above is not bound and isn't available to the Injector. Since ClassB can be created using assisted injection, though, it's able to creates one. It does and injects that into the instance of ClassA. In this case, both ClassA and ClassB instances have number of 1.

ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3);

As above, this creates new ClassB and ClassA instances for this new ClassC, each with number of 3.

If you want to address this, you need to change your factories to take the right dependencies and make those dependencies @Assisted.

ClassBFactory bFactory = injector.getInstance(ClassBFactory.class);
ClassB b = bFactory.create(2);
ClassAFactory aFactory = injector.getInstance(ClassAFactory.class);
ClassA a = aFactory.create(1, b); // <--
ClassCFactory cFactory = injector.getInstance(ClassCFactory.class);
ClassC c = cFactory.create(3, a); // <--

If you're only going to have one instance of each of these classes, this is better done in a Module as @Provides methods rather than using assisted injection and factories:

@Provides
ClassA provideClassA(ClassB b) {
  return new ClassA(1, b);
}

@Provides
ClassB provideClassB() {
  return new ClassB(2);
}

@Provides
ClassC provideClassC(ClassA a) {
  return new ClassC(3, a);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for explaining this so well. I was very confused by this. The providers using the @Provides annotation works well for me thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.