New in git head: OptionalBinder

2,978 views
Skip to first unread message

Sam Berlin

unread,
Apr 1, 2014, 8:33:41 PM4/1/14
to google...@googlegroups.com
Ever wanted to:
  1) Set default binding in a library or framework that users can change?
  2) Inject something into a library or framework that isn't required, but would be better if a user could set it?
  3) Inject an optional value into a constructor?

OptionalBinder lets you do that.  Christian just pushed out some of our recent internal changes, including the introduction of OptionalBinder to the multibindings extension.

Some example usage:

 1) Setting a default binding that a user can override:
   Library: OptionalBinder.newOptionalBinder(binder(), Foo.class)
                   .setDefault().to(DefaultFoo.class);
   At this point, code can inject 'Foo' (or Optional<Foo>), and will get a DefaultFoo.

   User: OptionalBinder.newOptionalBinder(binder(), Foo.class)
                  .setBinding().to(CustomFoo.class); 
   Once a user calls setBinding(), injections of Foo (or Optional<Foo>) will start providing CustomFoo instead.

 2) Setting a binding that isn't required (e.g, no default value), but a user can supply.
    Library: OptionalBinder.newOptionalBinder(binder(), Foo.class);
   At this point, code can't inject Foo -- it will fail saying the binding isn't supplied.  Code can (and should) inject Optional<Foo>, though, and the optional will be absent.

   User: OptionalBinder.newOptionalBinder(binder(), Foo.class)
                  .setBinding().to(CustomFoo.class); 
   Once a user calls setBinding(), Optional<Foo> will be present and supply the CustomFoo.  Also, Foo can be directly injected now (although only user could should do that, since the user code is the only part that guarantees the binding will be there -- library code should still inject the Optional<Foo>).

Please report back if you have any issues or suggestions for improvement.

Thanks!

 sam

Filipe Sousa

unread,
Apr 2, 2014, 9:20:40 AM4/2/14
to google...@googlegroups.com
That's excellent
 


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice...@googlegroups.com.
To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/d/optout.



--
Filipe Sousa

Sam Berlin

unread,
Apr 2, 2014, 9:41:45 AM4/2/14
to google...@googlegroups.com
I've been thinking also about some kind of syntax like:  

MyModule extends AbstractModule {
 configure() { install(Multibindings.forModule(this)); }
 @OptionalProvides(DEFAULT) Foo provideDefaultFoo(..) { .. }
 @OptionalProvides(ACTUAL) Foo provideActualFoo(..) { .. }
 @SetProvides Foo provideItemForSetOfFoo(..) { .. }
 @MapProvides MapBinderEntry<Key, Foo> provideItemForMapOfKeyToFoo(..) { .. }
}

... need to experiment and see if those pan out.

 sam

Tavian Barnes

unread,
Apr 2, 2014, 10:37:52 AM4/2/14
to google...@googlegroups.com
I like it, similar to what Dagger has for @Provides(SET).

I also really like OptionalBinder.  Previously I used Modules.override(new OverridableBindingsModule(), ...) to provide default implementations.

Nate Bauernfeind

unread,
Apr 2, 2014, 1:46:34 PM4/2/14
to google...@googlegroups.com

I'm very excited about this; are you going to cut another beta release anytime soon?

Is it possible to extend this to use other wrapper classes? I'd like to add this to the Scala-Guice project for Scala's option class if possible.

--

Sam Berlin

unread,
Apr 2, 2014, 1:57:48 PM4/2/14
to google...@googlegroups.com
No way to extend it to provide other wrapper classes, no.  Although you could pretty easily write a wrapper around it (e.g, ScalaOptionBinder) that binds your custom wrapper to something that delegates to Optional<T> (and Optional<Provider<T>, etc.).  That'd require users to use your wrapping binder, though.   If you don't want to do that, you could use the Guice SPI to analyze all module elements & automatically create new bindings for anything that's an OptionalBinderBinding, delegating to the actual/default values.

The latest push included one commit that we had to rollback internally (we're not exactly sure what's going wrong, but it caused a few tests in one project to become flaky).. so we could theoretically cut a new beta with the rollback.  But hopefully we can figure out what's going wrong, fix it, and cut a better beta.

 sam

Christian Gruber

unread,
Apr 2, 2014, 2:08:15 PM4/2/14
to google...@googlegroups.com
Yeah - I can see Dagger borrowing back the approach for Optional
provides, though having no access to an Optional in dagger is tricky
(targeting Java6 without non-JSR dependencies, like Guava). We'll have
to figure it out. At least we might be able to do the default approach
though.

c.

On 2 Apr 2014, at 7:37, Tavian Barnes wrote:

> I like it, similar to what Dagger has for @Provides(SET).
>
> I also really like OptionalBinder. Previously I used
> Modules.override(new
> OverridableBindingsModule(), ...) to provide default implementations.
>
> On Wednesday, 2 April 2014 09:41:45 UTC-4, Sam Berlin wrote:
>>
>> I've been thinking also about some kind of syntax like:
>>
>> MyModule extends AbstractModule {
>> configure() { install(Multibindings.forModule(this)); }
>> @OptionalProvides(DEFAULT) Foo provideDefaultFoo(..) { .. }
>> @OptionalProvides(ACTUAL) Foo provideActualFoo(..) { .. }
>> @SetProvides Foo provideItemForSetOfFoo(..) { .. }
>> @MapProvides MapBinderEntry<Key, Foo> provideItemForMapOfKeyToFoo(..)
>> {
>> .. }
>> }
>>
>> ... need to experiment and see if those pan out.
>>
>> sam
>>
>>
>> On Wed, Apr 2, 2014 at 9:20 AM, Filipe Sousa
>> <nat...@gmail.com<javascript:>
>>> wrote:
>>
>>> That's excellent
>>>
>>>
>>>
>>> On Wed, Apr 2, 2014 at 1:33 AM, Sam Berlin
>>> <sbe...@gmail.com<javascript:>
>>>> wrote:
>>>
>>>> Ever wanted to:
>>>> 1) Set default binding in a library or framework that users can
>>>> change?
>>>> 2) Inject something into a library or framework that isn't
>>>> required,
>>>> but would be better if a user could set it?
>>>> 3) Inject an optional value into a constructor?
>>>>
>>>> OptionalBinder lets you do that. Christian just pushed out some of
>>>> our
>>>> recent internal changes, including the introduction of
>>>> OptionalBinder to
>>>> the multibindings extension.
>>>>
>>>> Some example usage:
>>>>
>>>> 1) Setting a default binding that a user can override:
>>>> Library: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>>> * .setDefault().to(DefaultFoo.class);*
>>>> At this point, code can inject '*Foo*' (or *Optional<Foo>)*, and
>>>> will get a *DefaultFoo.*
>>>>
>>>> User: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>>> * .setBinding().to(CustomFoo.class);*
>>>> Once a user calls setBinding(), injections of *Foo* (or
>>>> *Optional<Foo>)* will start providing *CustomFoo* instead.
>>>>
>>>> 2) Setting a binding that isn't required (e.g, no default value),
>>>> but a
>>>> user can supply.
>>>> Library: *OptionalBinder.newOptionalBinder(binder(), Foo.class);*
>>>> At this point, code *can't* inject *Foo* -- it will fail saying the
>>>> binding isn't supplied. Code *can* (and should) inject
>>>> *Optional<Foo>*,
>>>> though, and the optional will be absent.
>>>>
>>>> User: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>>> * .setBinding().to(CustomFoo.class);*
>>>> Once a user calls setBinding(), *Optional<Foo>* will be present and
>>>> supply the *CustomFoo*. Also, *Foo* can be directly injected now
>>>> (although only user could should do that, since the user code is
>>>> the only
>>>> part that guarantees the binding will be there -- library code
>>>> should still
>>>> inject the* Optional<Foo>*).
>>>>
>>>> Please report back if you have any issues or suggestions for
>>>> improvement.
>>>>
>>>> Thanks!
>>>>
>>>> sam
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google
>>>> Groups "google-guice" group.
>>>> To unsubscribe from this group and stop receiving emails from it,
>>>> send
>>>> an email to google-guice...@googlegroups.com <javascript:>.
>>>> To post to this group, send email to
>>>> google...@googlegroups.com<javascript:>
>>>> .
>>>> Visit this group at http://groups.google.com/group/google-guice.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>>
>>>
>>> --
>>> Filipe Sousa
>>>
>>> --
>>> You received this message because you are subscribed to the Google
>>> Groups
>>> "google-guice" group.
>>> To unsubscribe from this group and stop receiving emails from it,
>>> send an
>>> email to google-guice...@googlegroups.com <javascript:>.
>>> To post to this group, send email to
>>> google...@googlegroups.com<javascript:>
>>> .
>>> Visit this group at http://groups.google.com/group/google-guice.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>
>
> --
> You received this message because you are subscribed to the Google
> Groups "google-guice" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to google-guice...@googlegroups.com.
> To post to this group, send email to google...@googlegroups.com.
> Visit this group at http://groups.google.com/group/google-guice.
> For more options, visit https://groups.google.com/d/optout.


Christian Gruber :: Google, Inc. :: Java Core Libraries :: Dependency
Injection
email: cgr...@google.com :::: mobile: +1 (646) 807-9839

Christian Gruber

unread,
Apr 2, 2014, 2:09:47 PM4/2/14
to google...@googlegroups.com
Let's get the OSGI fix in, and THEN cut beta5.

c.

On 2 Apr 2014, at 10:57, Sam Berlin wrote:

> No way to extend it to provide other wrapper classes, no. Although
> you
> could pretty easily write a wrapper around it (e.g, ScalaOptionBinder)
> that
> binds your custom wrapper to something that delegates to Optional<T>
> (and
> Optional<Provider<T>, etc.). That'd require users to use your
> wrapping
> binder, though. If you don't want to do that, you could use the
> Guice SPI
> to analyze all module elements & automatically create new bindings for
> anything that's an OptionalBinderBinding, delegating to the
> actual/default
> values.
>
> The latest push included one
> commit<https://code.google.com/p/google-guice/source/detail?r=bab9b6082ff7c3aefac2dc8c7de0468fe60fe8f6>that
>>> Library: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>> * .setDefault().to(DefaultFoo.class);*
>>> At this point, code can inject '*Foo*' (or *Optional<Foo>)*, and
>>> will
>>> get a *DefaultFoo.*
>>>
>>> User: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>> * .setBinding().to(CustomFoo.class);*
>>> Once a user calls setBinding(), injections of *Foo* (or
>>> *Optional<Foo>)* will start providing *CustomFoo* instead.
>>>
>>> 2) Setting a binding that isn't required (e.g, no default value),
>>> but a
>>> user can supply.
>>> Library: *OptionalBinder.newOptionalBinder(binder(), Foo.class);*
>>> At this point, code *can't* inject *Foo* -- it will fail saying the
>>> binding isn't supplied. Code *can* (and should) inject
>>> *Optional<Foo>*,
>>> though, and the optional will be absent.
>>>
>>> User: *OptionalBinder.newOptionalBinder(binder(), Foo.class)*
>>> * .setBinding().to(CustomFoo.class);*
>>> Once a user calls setBinding(), *Optional<Foo>* will be present and
>>> supply the *CustomFoo*. Also, *Foo* can be directly injected now
>>> (although only user could should do that, since the user code is the
>>> only
>>> part that guarantees the binding will be there -- library code
>>> should still
>>> inject the* Optional<Foo>*).

Thomas Suckow

unread,
Apr 3, 2014, 1:53:41 AM4/3/14
to google...@googlegroups.com
We can likely make this work for scala-guice in a similar manner to the Multibinder wrapper.

Thomas Suckow



To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an

To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice+unsubscribe@googlegroups.com.

To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
For more options, visit https://groups.google.com/d/optout.


Christian Gruber :: Google, Inc. :: Java Core Libraries :: Dependency Injection
email: cgr...@google.com :::: mobile: +1 (646) 807-9839

--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice+unsubscribe@googlegroups.com.

Sam Berlin

unread,
Jul 14, 2014, 1:28:19 PM7/14/14
to google...@googlegroups.com
FYI,  I submitted an improvement to this the other day so that when frameworks call OptionalBinder.newOptionalBinder(binder(), Foo.class), then users can supply the binding by just calling bind(Foo.class).to(FooImpl.class).  Frameworks can inject Optional<Foo>, which will be absent if users didn't Foo to anything, and present with the user's bound value if they did bind it.

This should allow people to easily swap out @com.google.inject.Inject(optional=true) Foo with @javax.inject.Inject Optional<Foo>, and also make it easier to use optional injection in constructors.

sam
Reply all
Reply to author
Forward
0 new messages