38

After spending a ludicrous amount of time trying to figure out why my dagger injections weren't working; I realised that the "object" type in Kotlin was the problem.

The following did not work, the injected "property" was null.

object SomeSingleton {

    @Inject
    lateinit var property: Property

    init {
        DaggerGraphController.inject(this)
    }
}

However, the following DID work just fine:

class NotSingleton {

    @Inject
    lateinit var property: Property

    init {
        DaggerGraphController.inject(this)
    }
}

I tried google, I tried the documentation but I could not pin point the reason behind this. Also note that I havent tried this with JAVA, JAVA doesnt have the concept of singletons built in anyway.

Why is this the case? Why is a kotlin singleton unable to inject members but a regular non-singleton class can?

Nihilanth
  • 401
  • 1
  • 4
  • 8

5 Answers5

17

If you look into kotlin bytecode you'll find that the code you've written is translated into following:

public final class SomeSingleton {
    public static LProperty; property // <- Notice static field here

    public final getProperty()LProperty
    ...

    public final setProperty(LProperty)V
    ...
}

As you can see the actual field is static which makes it uneligible for instance injection. You may try to move @Inject annotation onto setter method by doing so:

object SomeSingleton {
    @set:Inject
    lateinit var property: Property
    ...
}
Alex
  • 7,460
  • 2
  • 40
  • 51
  • 5
    this still didnt work, the first time around I got an uninitialized field error...after that, a NoClassDefFoundError i even tried with @field:Inject, same negative result unfortunately – Nihilanth Jan 24 '18 at 03:47
  • 3
    Doen't work for me. Exception during compilation `Caused by: java.lang.IllegalArgumentException at com.google.common.base.Preconditions.checkArgument(Preconditions.java:128) at dagger.internal.codegen.writing.InjectionMethod.invoke(InjectionMethod.java:112) at dagger.internal.codegen.writing.InjectionMethods$InjectionSiteMethod.invoke(InjectionMethods.java:358)` – Alex May 07 '20 at 14:32
12

A workaround for this can be to extend a BaseClass that consists of the fields to be injected.

object SomeSingleton : BaseClass {
    ...
    ...
}

open class BaseClass{
    @Inject
    lateinit var property: Property

    init{
        YourDaggerComponent.inject(this)
    }

}

This does work, although this would leak this, which comes up as an android studio warning, to get rid of that make the Base class abstract and instead inject the fields in your original object class

object SomeSingleton : BaseClass {
    ...
    ... 
 // Add the init block here instead of the base class
 init{
        YourDaggerComponent.inject(this)
    }
}

abstract class BaseClass{
    @Inject
    lateinit var property: Property
    
   //Remove the init block from here

}

And you Dagger AppComponent interface can be like, any of those function def should work

interface Component{
    fun inject(someSingleton : SomeSingleton)
    //OR
    fun inject(baseClass: BaseClass)
}

I hope this helps....

Vishal Ambre
  • 403
  • 4
  • 9
7

I tried to use dagger.Lazy<YourClass> and it works

 @set:Inject
lateinit var authClient: dagger.Lazy<PlatformAuthClient>
Dennis Zinkovski
  • 1,821
  • 3
  • 25
  • 42
  • @CaferMertCeyhan, what do you mean by doesn't work? What happens instead? – Tamás Barta Nov 05 '19 at 13:37
  • 7
    I just get this error when trying to inject into a kotlin object: `Dagger does not support injection into Kotlin objects`. This is on Dagger 2.27. – mco May 30 '20 at 04:57
  • Doesn't work for me, exception returns: lateinit property [variable] has not been initialized – Jakub Kostka Jan 06 '21 at 10:32
0

You may still need the @Singleton decorator on top of your object definition. That decorator doesn't make your class 'singleton', it's just used by Dagger to get all the dependencies in the same spot.

NicolasKittsteiner
  • 4,280
  • 1
  • 20
  • 17
0

This was previously allowed by a bug in Dagger. As others described, it is because the properties in a kotlin object are backed by static fields.

See https://github.com/google/dagger/issues/1665 for the fix. It was fixed in 2.27.

karl
  • 3,544
  • 3
  • 30
  • 46