Bu yazıda Android’de dependency injection için kullandığımız Dagger2′ de yer alan @Qualifier
ve @Named
annotationlarından bahsedeceğim.
Neden @Named annotation ihtiyaç duyarız?
Dagger2’de kullanılmak istenen nesne @Provides
annotation ile provide edilip sonrasında kullanılmak istenen yerde @Inject
annotation ile inject ediliyor. Ancak @Provides
annotation bir kısıtı var o da aynı tipe sahip nesneyi sadece bir kere provide edebilmesi. İşte bu probleme çözüm için @Named
annotation kullanacağız. Örneğin;
1 |
data class Car(val type: String) |
üstteki gibi basit bir yapıda olan Car sınıfımız olsun ve type adında bir property alsın.
Biz bu sınıftan örneğin MainActivity içerisinde 2 tane nesneyi @Inject
annotation kullanarak alalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MainActivity : AppCompatActivity() { @Inject lateinit var sedan: Car @Inject lateinit var sports: Car override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(...) } } |
CarModule.kt
dosyasında da alttaki gibi provide ettiğimizi düşünün.
1 2 3 4 5 6 7 8 9 |
@Module class CarModule { @Provides fun provideSedan(): Car = Car("Sedan") @Provides fun provideSports(): Car = Car("Sports") } |
Compile ettiğinizde alttaki gibi bir hata ile karşı karşıya geleceksiniz.
1 2 |
error: [Dagger/DuplicateBindings] packagename.example.model.Car is bound multiple times: |
Bu hatanın sebebi üstte bahsettiğim gibi @Provides
annotation sadece tek bir nesneyi provide edebilmesi. Ama gördüğünüz üzere aynı sınıftan 2 farklı nesneye ihtiyacım var ve bunları provide edebiliyor olmam gerekiyor. Çözümü için @Named
annotation kullanacağız.
CarModule.kt
dosyasını alttaki gibi güncellersek;
1 2 3 4 5 6 7 8 9 10 11 |
@Module class CarModule { @Provides @Named("sedan") fun provideSedan(): Car = Car("Sedan") @Provides @Named("sports") fun provideSports(): Car = Car("Sports") } |
Bu işlemden sonra MainActivity üzerinde inject ettiğimiz değerlere yine @Named
ile hangisini istediğimizi söylüyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class MainActivity : AppCompatActivity() { @Inject @field:Named("sedan") lateinit var sedan: Car @Inject @field:Named("sports") lateinit var sports: Car override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(...) } } |
Artık Dagger bunların farklı 2 nesne olduğunu anlayacak ve inject işleminde herhangi bir hata ile karşılaşmayacaksınız.
İşin biraz daha derinine inelim ve @Named
annotation kaynak kodlarına bakalım.
1 2 3 4 5 6 7 |
@Qualifier @Documented @Retention(RUNTIME) public @interface Named { /** The name. */ String value() default ""; } |
Burada dikkatinizi çekmek istediğim @Qualifier
annotation.
Gördüğünüz üzere @Named
annotation aslında bir qualifier. Yani aslında işin temelini @Qualifier
annotation oluşturuyor.
@Qualifier
javax.inject
package altında geliyor ve istenilen nesneyi nitelemek(qualifiy) için kullanılıyor. Yani sizde @Qualifier
sayesinde kendi custom annotation oluşturabilirsiniz.
Şimdi gelin kendi @Named
annotationımızı yazalım ve adını da @CustomNamed
yapalım.
Bunun için CustomNamed.kt
adında bir dosya oluşturun ve alttaki kodları ekleyin.
1 2 3 |
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class CustomNamed(val value: String = "") |
Bundan sonra tek yapmanız gereken @Named
annotationları @CustomNamed
ile değiştirmek.
Siz de ihtiyacınıza göre kendi custom qualifier larınızı oluşturabilirsiniz.