With the recent versions of dagger 2 one of the improvements made are the possibility of having static provide methods. Simply so:
#Provides
static A providesA() {
return A();
}
I was wondering how does one go about doing this in kotlin? I've tried
#Module
class AModule {
companion object {
#JvmStatic
#Provides
fun providesA(): A = A()
}
}
But I get the error message:
#Provides methods can only be present within a #Module or #ProducerModule
I'm guessing there's something going on here with the companion object, however I'm quite new to Kotlin and I'm unsure of how one can do this. Is it even possible?
Thanks!
Although I think zsmb13's solution is better, I found another solution which works
#Module
class AModule {
#Module
companion object {
#JvmStatic
#Provides
fun providesA(): A = A()
}
// add other non-static provides here
}
However, note that there will be two generated classes: AModule_ProvidesAFactory and AModule_Companion_ProvidesAFactory rather than the one AModule_ProvidesAFactory class for the case with an object instead of a class with a companion object
I can't test it right now, but I think this should work:
#Module
object AModule {
#JvmStatic
#Provides
fun providesA(): A = A()
}
Now Dagger2 (version 2.26) support companion objects in #Module annotated classes in kotlin withthouh #Module and #JvmStatic annotations
Better support for binding declarations within Kotlin companion
objects of #Module annotated classes.
Update dagger dependencies to 2.26 version as
def dagger_version = "2.26"
//dagger
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
//If you're using classes in dagger.android you'll also want to include:
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
so now you can use
#Module
class AModule {
companion object {
#Provides
fun providesA(): A = A()
}
}
Important: Soon, adding #Module on companion objects in #Module classes will raise an error.
Note: For backwards compatibility, we still allow #Module on the
companion object and #JvmStatic on the provides methods. However,
#Module on the companion object is now a no-op and all of its
attributes (e.g. "includes") will be ignored. In future releases, we
will make it an error to use #Module on a companion object.
A great explanation which seems to be Google-approved is at https://github.com/google/dagger/issues/900
Specifically, see:
Static provides can be achieved via #JvmStatic. There are two scenarios I see this come up:
top-level objects
#Module object DataModule {
#JvmStatic #Provides fun
provideDiskCache() = DiskCache()
}
If you have an existing class
module, things get a bit weirder
#Module abstract class DataModule {
#Binds abstract fun provideCache(diskCache: DiskCache): Cache
#Module
companion object {
#JvmStatic #Provides fun provideDiskCache() = DiskCache()
}
}
The way this works is as follows:
the companion object must also be annotated as #Module under the hood,
the kotlin compiler will duplicate those static provides methods into
the DataModule class. Dagger will see those and treat them like
regular static fields. Dagger will also see them in the companion
object, but that "module" will get code gen from dagger but be marked
as "unused". The IDE will mark this as such, as the provideDiskCache
method will be marked as unused. You can tell IntelliJ to ignore this
for annotations annotated with #Provides via quickfix
For the static only approach, I like the solution of zsmb13.
However, I came here because I wanted to combine #Provides and #Binds within one module.
This is not directly possible but with two nested modules (as Omar Al Halabi pointed out).
I took a slightly different approach for combining #Provides and #Binds:
#Module(includes = [MyModule.Bindings::class])
object MyModule {
#Module
interface Bindings {
#Binds
fun bindA(a: AImpl): A
}
#Provides
#JvmStatic
fun provideB(): B = BImpl()
}
The differences are:
The outer module is an object providing the static functions. This spares using the unintentional companion object.
The inner module holds the abstract bindings. I can use an interface here, which spares the abstract modifier in both the class and the function.
The outer module includes the inner module so I don't have to include the inner module elsewhere.
Related
I'm building an ORM for use with jasync-sql in Kotlin and there's a fundamental problem that I can't solve. I think it boils down to:
How can one instantiate an instance of a class of type T, given a
non-reified type parameter T?
The well known Spring Data project manages this and you can see it in their CrudRepository<T, ID> interface that is parameterised with a type parameter T and exposes methods that return instances of type T. I've had a look through the source without much success but somewhere it must be able to instantiate a class of type T at runtime, despite the fact that T is being erased.
When I look at my own AbstractRepository<T> abstract class, I can't work out how to get a reference to the constructor of T as it requires accessing T::class.constructors which understandably fails unless T is a reified type. Given that one can only used reified types in the parameters of inline functions, I'm a bit lost as to how this can work?
On the JVM, runtime types of objects are erased, but generic types on classes aren't. So if you're working with concrete specializations, you can use reflection to retrieve the type parameter:
import java.lang.reflect.*
abstract class AbstractRepository<T>
#Suppress("UNCHECKED_CAST")
fun <T> Class<out AbstractRepository<T>>.repositoryType(): Class<T> =
generateSequence<Type>(this) {
(it as? Class<*> ?: (it as? ParameterizedType)?.rawType as? Class<*>)
?.genericSuperclass
}
.filterIsInstance<ParameterizedType>()
.first { it.rawType == AbstractRepository::class.java }
.actualTypeArguments
.single() as Class<T>
class IntRepository : AbstractRepository<Int>()
class StringRepository : AbstractRepository<String>()
interface Foo
class FooRepository : AbstractRepository<Foo>()
class Bar
class BarRepository : AbstractRepository<Bar>()
fun main() {
println(IntRepository::class.java.repositoryType())
println(StringRepository::class.java.repositoryType())
println(FooRepository::class.java.repositoryType())
println(BarRepository::class.java.repositoryType())
}
class java.lang.Integer
class java.lang.String
interface Foo
class Bar
In your own CrudRepository you can add a companion object with an inline fun which is responsible to instantiate your repository by passing to it the corresponding class.
class MyCrudRepository<T> protected constructor(
private val type: Class<T>,
) {
companion object {
inline fun <reified T : Any> of() = MyCrudRepository(T::class.java)
}
fun createTypeInstance() = type::class.createInstance()
}
I'm trying to create a Map that contains generic-parameterized types. For example:
abstract class Foo {
companion object {
val fooInjectors = HashMap<Class<T: Foo>, Injector<T: Foo>>()
}
}
The idea is to have fooInjectors (which would be static in Java or in a companion object in Kotlin) contain a cache of sub-classes of Foo and their corresponding Injector.
Unfortunately, I can't get this to compile. I'd very much appreciate it if someone would help me figure out the syntax for this!
As far as I know, you are trying to do something that is impossible in Kotlin. The companion object is a singleton and it doesn't make sense to generify a singleton as there will not be any further objects created hence generic types are irrelevant. So you can't generify the property you declared because it's in the companion object.
However, one way you could make this working is using a backing function. This backing function should annotate with declaration-site variance.
This simply means we tell the compiler that we only return a type T from the method (and don't consume). That allows us to use subtypes and the supertype of the T if required. This is called covariance.
You can look at the docs to understand it further - https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance
Here's what I meant.
interface Injector<T>
class InjectorImpl<T> : Injector<T>
abstract class Foo {
companion object {
val fooInjectors = createMap<Foo>()
private fun <T> createMap(): HashMap<Class<out T>, Injector<out T>> {
return HashMap()
}
}
}
class Bar: Foo()
object Runner {
#JvmStatic
fun main(args: Array<String>) {
Foo.fooInjectors[Bar::class.java] = InjectorImpl<Bar>()
Foo.fooInjectors[Foo::class.java] = InjectorImpl<Bar>()
}
}
In Kotlin, a common use for object is using it for singletons, like:
object MyObject {
...
}
However, when using micronaut framework, the official documentation recommends using something like this:
#Singleton
class V8Engine : Engine {
override var cylinders = 8
override fun start(): String {
return "Starting V8"
}
}
Why can't I use simply object instead of using annotation #Singleton with a class?
With a #Singleton, Micronaut can automatically manage dependencies between beans. If you go with the other class in https://docs.micronaut.io/latest/guide/ioc.html#beans, translated to Kotlin:
#Singleton
class Vehicle(private val engine: Engine) {
public fun start() = engine.start()
}
It can't be just an object because takes a parameter.
This parameter is discovered by Micronaut to be the singleton instance of V8Engine, which needs that to be a #Singleton and not an object.
Of course, in this case you could just directly use V8Engine in Vehicle; but it's easier to change e.g. if you want Engine not to be a singleton anymore.
Why can't I use simply object instead of using annotation #Singleton
with a class?
You can use object instead of using #Singleton with a class. Micronaut won't manage instances for you, but that is allowed.
I am reading the Kotlin in Action book and trying to understand Companion Objects better, are there any other uses for Companion Ojbects other than adding method implementations from an interface/abstract class?
I came across a way of instantiating an object which only works if the class is abstract:
fun main(args: Array<String>) {
Fruit.showColor()
}
class Fruit(val name: String) {
companion object : Apple()
}
abstract class Apple {
fun showColor(){
print("I am an apple")
};
}
My mental model for companion object is language level support for safe singletons. i.e. instead of static methods on a class for Factory or Util methods, you can provide those related methods on the Singleton companion object.
The Companion status gives you a lot of default scoping wins that are similar to the java class with static methods.
Your example seems invalid, because why is the Fruit "singleton" an Apple?
I'm using Dagge2 in my kotlin project with pom file. I worked with Dagger in my android projects and it worked fine. But somehow I don't understand why Dagger generating multiple instances of my every object in kotlin.
Below is my Component
#Singleton
#Component(modules = [MeterCollectionModule::class])
interface AppComponent {
fun meterCollection(): MeterCollection
}
And this is my Module
#Module(includes = [UtilModule::class])
class MeterCollectionModule {
#Singleton
#Provides
fun meterCollection() = MeterCollection()
}
That's how I'm building my AppComponent
DaggerAppComponent
.builder()
.build()
.inject(this)
I debug my code and see, every time I inject MeterCollection class it gives me new object.
The #Singleton annotation (as well as any other scope annotation) is taken into account only if you're reusing the same component. In other words, Dagger is not able to respect your #Singleton scope across different instances of the same component.
Hence, in order to inject the same MeterCollection instance, you should also reuse the same DaggerAppComponent instance (e.g., by putting it in an instance variable).
Singleton and Provider annotations won't work with object which is created every time method is called. I'd refactor the class to:
#Module(includes = [UtilModule::class])
class MeterCollectionModule {
val myMeterConnection = MeterConnection()
#Singleton
#Provides
fun meterCollection(){
return myMeterConnection
}
(which is identical solution to what #user2340612 proposed)