codingwell / scala-guice

Scala extensions for Google Guice
Apache License 2.0
341 stars 44 forks source link

Can someone Inject something that is Optional ? #22

Closed Maatary closed 10 years ago

Maatary commented 10 years ago

bind([Option [T]]).instance[Some(T)]

tsuckow commented 10 years ago

Hmm. This might take some thinking.

nbauernfeind commented 10 years ago

Per your example; you can do exactly that.

object T {
  def main(args: Array[String]) {
    val a = Guice.createInjector(new Module).instance[A]
    println(a.string)
    println(a.b)
  }

  class A @Inject() (val string: Option[String], val b: Option[B])
  class B

  class Module extends ScalaModule {
    def configure() {
      bind[Option[String]].toInstance(Some("Hello World"))
      bind[Option[B]].toInstance(None)
    }
  }
}

Now... if you're talking about truly optional dependencies; you can see that Guice barely supports it. They suggest to create a container class. Thus, it seems to be possible to create a generic optional provider class such as this:

object T {
  def main(args: Array[String]) {
    val a = Guice.createInjector(new Module).instance[A]
    println(a.string)
    println(a.b)
  }

  class A @Inject() (val string: Option[String], val b: Option[B])
  class B

  class Module extends ScalaModule {
    def configure() {
      binder.requireExplicitBindings() // Guice will try to create anything it can such as an instance of B even if unbound
      bind[A].asEagerSingleton()
      bind[String].toInstance("Hello World")

      // We must register providers for each optional injection
      bindOpt[String]
      bindOpt[B]
    }

    private[this] def bindOpt[T: Manifest] = bind[Option[T]].toProvider[OptionalProvider[T]]
  }

  class OptionalProvider[T] @Inject() extends Provider[Option[T]] {
    @Inject(optional = true)
    val t: T = null.asInstanceOf[T]
    def get(): Option[T] = Option(t)
  }
}

And the downside is, obviously, that you have to pre-register every optional thing you might inject. Another downside is that any annotated constraints on the T can't be automagically picked up (like an @Named Option injection).

Unfortunately, Guice was not written with the idea of extending it via additional resolvers (akin to the way Jackson works). So it is not possible for any magic to automatically create Option[T] injections for any T.

Maatary commented 10 years ago

Understood.

Many thanks such detailed and nit explanation. Appreciated.

On Monday, December 30, 2013, Nate Bauernfeind wrote:

Per your example; you can do exactly that.

object T { def main(args: Array[String]) { val a = Guice.createInjector(new Module).instance[A] println(a.string) println(a.b) }

class A @Inject() (val string: Option[String], val b: Option[B]) class B

class Module extends ScalaModule { def configure() { bind[Option[String]].toInstance(Some("Hello World")) bind[Option[B]].toInstance(None) } }}

Now... if you're talking about truly optional dependencies; you can see that Guice barely supports ithttps://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_can_I_inject_optional_parameters_into_a_constructor?. They suggest to create a container class. Thus, it seems to be possible to create a generic optional provider class such as this:

object T { def main(args: Array[String]) { val a = Guice.createInjector(new Module).instance[A] println(a.string) println(a.b) }

class A @Inject() (val string: Option[String], val b: Option[B]) class B

class Module extends ScalaModule { def configure() { binder.requireExplicitBindings() // Guice will try to create anything it can such as an instance of B even if unbound bind[A].asEagerSingleton() bind[String].toInstance("Hello World")

  // We must register providers for each optional injection
  bindOpt[String]
  bindOpt[B]
}

private[this] def bindOpt[T: Manifest] = bind[Option[T]].toProvider[OptionalProvider[T]]

}

class OptionalProvider[T] @Inject() extends Provider[Option[T]] { @Inject(optional = true) val t: T = null.asInstanceOf[T] def get(): Option[T] = Option(t) }}

And the downside is, obviously, that you have to pre-register every optional thing you might inject. Another downside is that any annotated constraints on the T can't be automagically picked up (like an @Named Option injection).

Unfortunately, Guice was not written with the idea of extending it via additional resolvers (akin to the way Jackson works). So it is not possible for any magic to automatically create Option[T] injections for any T.

— Reply to this email directly or view it on GitHubhttps://github.com/codingwell/scala-guice/issues/22#issuecomment-31332472 .

tsuckow commented 10 years ago

Should revisit this now that https://github.com/google/guice/commit/842f351c4b2b3b0a90d9f3bcf164d8fd19aede6c has landed