Guice: differences between Singleton.class and @Singleton
They are nearly identical. The @Singleton
syntax is useful for annotating @Provides
methods, or annotating the class itself (though I prefer to keep my scoping annotations inside modules).
The difference lies in which key is marked Singleton, which has less to do with @Singleton
versus Singleton.class
(or Scopes.SINGLETON
, asEagerSingleton
, @Singleton
class annotations, or toInstance
implicit singletons) and more to do with what the default syntax makes easy. For example:
public class MyModule extends AbstractModule {
@Override public void configure() {
bind(A.class).to(AImpl.class).in(Singleton.class);
bind(B.class).to(BImpl.class);
bind(BImpl.class).in(Singleton.class);
}
@Provides @Singleton C provideC() { return new CImpl(); }
@Provides @Singleton D provideD(DImpl dImpl) { return dImpl; }
@Provides E provideE(EImpl eImpl) { return eImpl; }
@Provides @Singleton EImpl provideEImpl() { return new EImpl(); }
}
Above we've bound interface A
to class AImpl
, and interface B
to class BImpl
, but the behavior is different:
- Injecting
A
will retrieve the sameAImpl
instance every time. - Injecting
AImpl
will retrieve a differentAImpl
every time, all of which are different thanA
's instance. - Injecting
B
will retrieve the sameBImpl
instance every time. - Injecting
BImpl
will also retrieve that sameBImpl
instance thatB
injects.
As you can see, each key is different, and Guice will allow multiple implementation instances if only the interface is bound with Singleton. If you only ever inject A
and B
interfaces, the behavior looks identical, but if you inject both interfaces and implementations from the same Injector, you may see differing behavior.
Similar logic goes for @Provides
methods:
- Injecting
C
will always return the sameCImpl
instance. - Injecting
CImpl
will create a newCImpl
every time, unlessCImpl
has no injectable public zero-arg constructor—then the injection will fail. - Injecting
D
will always return the sameDImpl
instance. - Injecting
DImpl
will return a new instance every time, and each will be different than the one returned byD
. - Injecting
E
will return the sameEImpl
instance every time. - Injecting
EImpl
will also retrieve that same instanceE
injects.
This provides some flexibility. Imagine a hypothetical Cache
that keeps a certain number of most-recently-retrieved objects, where you want to have @User Cache
and @Product Cache
both injectable. If you bind(Cache.class).in(Singleton.class)
, you will have one Cache shared between the objects (and any bare Cache
injections), whereas if you bind(Cache.class).annotatedWith(User.class).to(Cache.class).in(Singleton.class)
then the annotated key is kept in singleton scope and each object type will have its own cache.
Related videos on Youtube
IAmYourFaja
my father is a principal at burgoyne intnl and got me this job programming lisp and development. I aspire to unittesting with a concentration in mobile platforms.
Updated on July 09, 2022Comments
-
IAmYourFaja almost 2 years
In Guice, what's the difference between:
// Inside your AbstractModule subclass: @Override public void configure() { bind(Service.class).to(ServiceImpl.class).in(Singleton.class); }
And:
@Override public void configure() { bind(Service.class).to(ServiceImpl.class); } @Provides @Singleton public ServiceImpl providesService() { return new ServiceImpl(); }
Are they both the same? When would you use one versus the other? Thanks in advance.
-
EFreak about 8 yearsI have a situation where they seem to behave differently. Using the bind statement did not restrict to just 1 instance. However, using @Singleton it worked.
-
Jeff Bowman about 8 years@EFreak: Updated answer. It's not exactly about the difference in syntax, but with a fresh look at the question, there's some subtlety there.
-
EFreak about 8 yearsThanks a ton! This is exactly the problem I was facing yesterday. Although, I have an edit suggestion. Will update the answer.