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.