There’s a bug in our code—do you spot it? It’s subtle, so you may need to look closely.
In order to help find it, let’s first create a deposit
command:
final class DepositCommand implements Command {
private final Database database;
private final Outputter outputter;
@Inject
DepositCommand(Database database, Outputter outputter) {
this.outputter = outputter;
this.database = database;
}
@Override
public Result handleInput(List<String> input) {
if (input.size() != 2) {
return Result.invalid();
}
Account account = database.getAccount(input.get(0));
account.deposit(new BigDecimal(input.get(1)));
outputter.output(account.username() + " now has: " + account.balance());
return Result.handled();
}
}
Let’s try adding this command with a @Binds
@IntoMap
method like
the commands above. Let’s put it in a module called UserCommandsModule
that
will hold all of the commands that deal with a specific user.
@Module
abstract class UserCommandsModule {
@Binds
@IntoMap
@StringKey("deposit")
abstract Command depositCommand(DepositCommand command);
}
Don’t forget to add UserCommandsModule
in the @Component
annotation of
CommandRouterFactory
. Run the application with the following commands:
deposit colin 2
login colin
The second command shows that colin
has a balance of 0. How could that be?
To help make this clearer, add System.out.println("Creating a new " + this);
statements to the Database
, LoginCommand
, and DepositCommand
constructors.
Rerun the application and you’ll see that two databases are being created.
Dagger by default provides one Database
object when LoginCommand
requests it
and another when DepositCommand
requests it.
In order to tell Dagger that they both need to share the same instance of
Database
, we annotate the Database
class with
@Singleton
. We also annotate our
@Component
type with @Singleton
to declare that instances of classes
annotated with @Singleton
should be shared among other objects that depend
on them in this component.
@Singleton
final class Database { ... }
@Singleton
@Component
interface CommandRouterFactory {
...
}
Try rerunning again. Now the login and deposit commands share a single
Database
instance.
CONCEPTS
@Singleton
instructs Dagger to create only one instance of the type for each instance of the component. It can be used on the class declaration of a type that has an@Inject
constructor, or on a@Binds
or@Provides
method.- It’s not yet clear why you have to annotate the component with
@Singleton
as well, but it will become clearer later.