An annotation processor library that automatically creates Hilt's `@Binds` functions and modules.

Overview

Generic badge  Generic badge  Generic badge  Generic badge 

HiltBinder

An annotation processor library that automatically creates Hilt's @Binds functions and modules.
If you think this library is useful, please press ⭐️ Star button at upside : )



# How to use

Can be used in android project with hilt applied. And add dependency like below code.

// build.gradle(:project)
repositories {
    google()
    mavenCentral()
}

// build.gradle(:app)
dependencies {

    def hiltBinderVersion = "1.3.0"
    implementation "com.smparkworld.hiltbinder:hiltbinder:$hiltBinderVersion"
    kapt "com.smparkworld.hiltbinder:hiltbinder-processor:$hiltBinderVersion"
}



# Basic usage

No longer need abstract module classes. Just add @HiltBinds and the Binds module will be created automatically.

interface TestUseCase {
    fun printTestString()
}

@HiltBinds
class TestUseCaseImpl @Inject constructor(
    private val testString: String
) : TestUseCase {

    override fun printTestString() {
        Log.d("Test!!", "TestString is $testString in UseCase.")
    }
}

And the processor automatically generates the following Java files:

@Module
@InstallIn(SingletonComponent.class)
abstract class TestUseCaseImpl_BindsModule {
    @Binds
    public abstract TestUseCase bindTestUseCaseImpl(TestUseCaseImpl target);
}



# Options

to

The return type of the Binds abstract function.

interface OtherSpec {
  ...
}

interface ToSampleModel {
    fun printTestString()
}

@HiltBinds(to = ToSampleModel::class)
class ToSampleModelImpl @Inject constructor(
    private val testString: String
) : OtherSpec, ToSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in ToSampleModelImpl class.")
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class ToSampleModelImpl_BindsModule {
  @Binds
  public abstract ToSampleModel bindToSampleModelImpl(ToSampleModelImpl target);
}

from

The argument type of the Binds abstract function. However, from an architectural point of view, this is not recommended.

@HiltBinds(from = FromSampleModelImpl::class)
interface FromSampleModel {
    fun printTestString()
}

class FromSampleModelImpl @Inject constructor(
    private val testString: String
) : FromSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in FromSampleModelImpl class.")
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class FromSampleModel_BindsModule {
  @Binds
  public abstract FromSampleModel bindFromSampleModel(FromSampleModelImpl target);
}

component

Specifies in which component the class to be returned will be installed.

interface ComponentSampleModel {
    fun printTestString()
}

@HiltBinds(component = ActivityRetainedComponent::class)
class ComponentSampleModelImpl @Inject constructor(
    private val testString: String
) : ComponentSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in ComponentSampleModelImpl class.")
    }
}
// generated code
@Module
@InstallIn(ActivityRetainedComponent.class)
abstract class ComponentSampleModelImpl_BindsModule {
  @Binds
  public abstract ComponentSampleModel bindComponentSampleModelImpl(ComponentSampleModelImpl target);
}

scope

To specify ranges separately, apply scope annotations as in the following code snippet. The reason this can work is that applying a scope to the implementing class works to keep the singleton in scope via the dagger.internal.DoubleCheck class within the Hilt.

interface ComponentSampleModel {
    fun printTestString()
}

@HiltBinds(component = ActivityRetainedComponent::class)
@ActivityRetainedScope
class ComponentSampleModelImpl @Inject constructor(
    private val testString: String
) : ComponentSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in ComponentSampleModelImpl class.")
    }
}

or

@HiltBinds
@Singleton
class SomethingSampleModelImpl @Inject constructor(
    private val testString: String
) : SomethingSampleModel {
  ...
}

qualifier

The Qualifier annotation to be applied to the return type.

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleQualifier1

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleQualifier2

interface QualifierSampleModel {
    fun printTestString()
}

@HiltBinds
@SampleQualifier1
class QualifierSampleModelImpl1 @Inject constructor(
    private val testString: String
) : QualifierSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifierSampleModelImpl1 class.")
    }
}

@HiltBinds
@SampleQualifier2
class QualifierSampleModelImpl2 @Inject constructor(
    private val testString: String
) : QualifierSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifierSampleModelImpl2 class.")
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifierSampleModelImpl1_BindsModule {
  @Binds
  @SampleQualifier1
  public abstract QualifierSampleModel bindQualifierSampleModelImpl1(QualifierSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class QualifierSampleModelImpl2_BindsModule {
  @Binds
  @SampleQualifier2
  public abstract QualifierSampleModel bindQualifierSampleModelImpl2(QualifierSampleModelImpl2 target);
}

named

The Qualifier annotation to be applied to the return type.

interface NamedSampleModel {
    fun printTestString()
}

@HiltBinds
@Named("model1")
class NamedSampleModelImpl1 @Inject constructor(
   private val testString: String
) : NamedSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedSampleModelImpl1 class.")
    }
}

@HiltBinds
@Named("model2")
class NamedSampleModelImpl2 @Inject constructor(
   private val testString: String
) : NamedSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedSampleModelImpl2 class.")
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSampleModelImpl1_BindsModule {
  @Binds
  @Named("model1")
  public abstract NamedSampleModel bindNamedSampleModelImpl1(NamedSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSampleModelImpl2_BindsModule {
  @Binds
  @Named("model2")
  public abstract NamedSampleModel bindNamedSampleModelImpl2(NamedSampleModelImpl2 target);
}



CAUTION HERE

parameter to and from must not be signed together. Either to or from must be used. If they are signed at the same time, throws an exception. Because dependency injection can be attempted from other unrelated classes as in the code below.

@HiltBinds(
    to = SampleModel::class,
    from = SampleModelImpl::class
)
interface SomethingClass    // throws an exception.



# MultiBinding

Set Multibinding - basics

You must use @HiltSetBinds to apply Set Multibinding.

interface SetSampleModel {
    fun printTestString()
}

@HiltSetBinds
class SetSampleModelImpl1 @Inject constructor(
    private val testString: String
) : SetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in SetSampleModelImpl1 class.")
    }
}

@HiltSetBinds
class SetSampleModelImpl2 @Inject constructor(
    private val testString: String
) : SetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in SetSampleModelImpl2 class.")
    }
}

// This is the code to get Set Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var sampleSet: @JvmSuppressWildcards Set<SetSampleModel>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        sampleSet.forEach {
            it.printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SetSampleModelImpl1_BindsModule {
  @Binds
  @IntoSet
  public abstract SetSampleModel bindSetSampleModelImpl1(SetSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SetSampleModelImpl2_BindsModule {
  @Binds
  @IntoSet
  public abstract SetSampleModel bindSetSampleModelImpl2(SetSampleModelImpl2 target);
}

Set Multibinding - qualifier

If you want to configure multiple Set Multibinding of the same type, you can use @Qualifier(javax.inject.Qualifier) annotations like this:

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleSetQualifierA

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleSetQualifierB

interface QualifiedSetSampleModel {
    fun printTestString()
}

@HiltSetBinds
@SampleSetQualifierA
class QualifiedSetSampleModelImpl1 @Inject constructor(
    private val testString: String
) : QualifiedSetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifiedSetSampleModelImpl1 class.")
    }
}

@HiltSetBinds
@SampleSetQualifierB
class QualifiedSetSampleModelImpl2 @Inject constructor(
    private val testString: String
) : QualifiedSetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifiedSetSampleModelImpl2 class.")
    }
}

// This is the code to get Set Multibinding - qualifier.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    @SampleSetQualifierA
    lateinit var sampleQualifiedSetA: @JvmSuppressWildcards Set<QualifiedSetSampleModel>

    @Inject
    @SampleSetQualifierB
    lateinit var sampleQualifiedSetB: @JvmSuppressWildcards Set<QualifiedSetSampleModel>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        sampleQualifiedSetA.forEach { it.printTestString() }
        sampleQualifiedSetB.forEach { it.printTestString() }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedSetSampleModelImpl1_BindsModule {
  @Binds
  @IntoSet
  @SampleSetQualifierA
  public abstract QualifiedSetSampleModel bindQualifiedSetSampleModelImpl1(QualifiedSetSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedSetSampleModelImpl2_BindsModule {
  @Binds
  @IntoSet
  @SampleSetQualifierB
  public abstract QualifiedSetSampleModel bindQualifiedSetSampleModelImpl2(QualifiedSetSampleModelImpl2 target);
}

Set Multibinding - named

If you want to configure multiple Set Multibinding of the same type, you can use @Named(javax.inject.Named) annotations like this:

interface NamedSetSampleModel {
    fun printTestString()
}

@HiltSetBinds
@Named("sampleNamedSetA")
class NamedSetSampleModelImpl1 @Inject constructor(
    private val testString: String
) : NamedSetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedSetSampleModelImpl1 class.")
    }
}

@HiltSetBinds
@Named("sampleNamedSetB")
class NamedSetSampleModelImpl2 @Inject constructor(
    private val testString: String
) : NamedSetSampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedSetSampleModelImpl2 class.")
    }
}

// This is the code to get Set Multibinding - named.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    @Named("sampleNamedSetA")
    lateinit var sampleNamedSetA: @JvmSuppressWildcards Set<NamedSetSampleModel>

    @Inject
    @Named("sampleNamedSetB")
    lateinit var sampleNamedSetB: @JvmSuppressWildcards Set<NamedSetSampleModel>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        sampleNamedSetA.forEach { it.printTestString() }
        sampleNamedSetB.forEach { it.printTestString() }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSetSampleModelImpl1_BindsModule {
  @Binds
  @IntoSet
  @Named("sampleNamedSetA")
  public abstract NamedSetSampleModel bindNamedSetSampleModelImpl1(NamedSetSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class NamedSetSampleModelImpl2_BindsModule {
  @Binds
  @IntoSet
  @Named("sampleNamedSetB")
  public abstract NamedSetSampleModel bindNamedSetSampleModelImpl2(NamedSetSampleModelImpl2 target);
}

Map Multibinding - basics

You must use @HiltMapBinds to apply Map Multibinding. And you must to add a Key annotation with hilt's @MapKey applied, as in the code below. You can use the @ClassKey, @StringKey, @IntKey, @LongKey provided by hilt.

interface MapStringKeySampleModel {
    fun printTestString()
}

@HiltMapBinds
@StringKey("model1")
class MapStringKeySampleModelImpl1 @Inject constructor(
    private val testString: String
) : MapStringKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapStringKeySampleModelImpl1 class.")
    }
}

@HiltMapBinds
@StringKey("model2")
class MapStringKeySampleModelImpl2 @Inject constructor(
    private val testString: String
) : MapStringKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapStringKeySampleModelImpl2 class.")
    }
}

// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var stringKeySampleMap: @JvmSuppressWildcards Map<String, Provider<MapStringKeySampleModel>>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        for ((k, v) in stringKeySampleMap) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapStringKeySampleModelImpl1_BindsModule {
  @Binds
  @IntoMap
  @StringKey("model1")
  public abstract MapStringKeySampleModel bindMapStringKeySampleModelImpl1(MapStringKeySampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class MapStringKeySampleModelImpl2_BindsModule {
  @Binds
  @IntoMap
  @StringKey("model2")
  public abstract MapStringKeySampleModel bindMapStringKeySampleModelImpl2(MapStringKeySampleModelImpl2 target);
}

Map Multibinding - custom key

And you can define and use map key annotations. In addition to enum classes, you can define other types.

enum class SampleType {
    SAMPLE1, SAMPLE2, DEFAULT
}

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class SampleMapCustomKey(val key: SampleType)

interface MapCustomKeySampleModel {
    fun printTestString()
}

@HiltMapBinds
@SampleMapCustomKey(SampleType.SAMPLE1)
class MapCustomKeySampleModelImpl1 @Inject constructor(
    private val testString: String
) : MapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapCustomKeySampleModelImpl1 class.")
    }
}

@HiltMapBinds
@SampleMapCustomKey(SampleType.SAMPLE2)
class MapCustomKeySampleModelImpl2 @Inject constructor(
    private val testString: String
) : MapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapCustomKeySampleModelImpl2 class.")
    }
}

// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var customKeySampleMap: @JvmSuppressWildcards Map<SampleType, Provider<MapCustomKeySampleModel>>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        for ((k, v) in customKeySampleMap) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapCustomKeySampleModelImpl1_BindsModule {
  @Binds
  @IntoMap
  @SampleMapCustomKey(key = SampleType.SAMPLE1)
  public abstract MapCustomKeySampleModel bindMapCustomKeySampleModelImpl1(MapCustomKeySampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class MapCustomKeySampleModelImpl2_BindsModule {
  @Binds
  @IntoMap
  @SampleMapCustomKey(key = SampleType.SAMPLE2)
  public abstract MapCustomKeySampleModel bindMapCustomKeySampleModelImpl2(MapCustomKeySampleModelImpl2 target);
}

Map Multibinding - complex custom key

You can use key annotations with multiple parameters as in the code below. Complex custom keys require dependencies from the auto-value and auto-value-annotation libraries. For more information, see References.

/***
 * Complex key require the following dependencies:
 *
 *   def autoValueVersion = "1.9"
 *   implementation "com.google.auto.value:auto-value:$autoValueVersion"
 *   implementation "com.google.auto.value:auto-value-annotations:$autoValueVersion"
 */
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey(unwrapValue = false)
annotation class SampleMapComplexKey(
    val key1: String,
    val key2: KClass<*>,
    val key3: Array<String>,
    val key4: IntArray,
    val key5: SampleType
)

interface MapComplexKeySampleModel {
    fun printTestString()
}

@HiltMapBinds
@SampleMapComplexKey(
    key1 = "sample1",
    key2 = MapComplexKeySampleModelImpl1::class,
    key3 = ["s1", "s2", "s3"],
    key4 = [1, 2, 3],
    key5 = SampleType.SAMPLE1
)
class MapComplexKeySampleModelImpl1 @Inject constructor(
    private val testString: String
) : MapComplexKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapComplexKeySampleModelImpl1 class.");
    }
}

@HiltMapBinds
@SampleMapComplexKey(
    key1 = "sample2",
    key2 = MapComplexKeySampleModelImpl2::class,
    key3 = ["s4", "s5", "s6"],
    key4 = [4, 5, 6],
    key5 = SampleType.SAMPLE2
)
class MapComplexKeySampleModelImpl2 @Inject constructor(
    private val testString: String
) : MapComplexKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in MapComplexKeySampleModelImpl2 class.");
    }
}

// This is the code to get Map Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var complexKeySampleMap: @JvmSuppressWildcards Map<SampleMapComplexKey, Provider<MapComplexKeySampleModel>>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        for ((k, v) in complexKeySampleMap) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MapComplexKeySampleModelImpl1_BindsModule {
  @Binds
  @IntoMap
  @SampleMapComplexKey(
          key1 = "sample1",
          key2 = MapComplexKeySampleModelImpl1.class,
          key3 = {"s1","s2","s3"},
          key4 = {1,2,3},
          key5 = SampleType.SAMPLE1
  )
  public abstract MapComplexKeySampleModel bindMapComplexKeySampleModelImpl1(MapComplexKeySampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class MapComplexKeySampleModelImpl2_BindsModule {
  @Binds
  @IntoMap
  @SampleMapComplexKey(
          key1 = "sample2",
          key2 = MapComplexKeySampleModelImpl2.class,
          key3 = {"s4","s5","s6"},
          key4 = {4,5,6},
          key5 = SampleType.SAMPLE2
  )
  public abstract MapComplexKeySampleModel bindMapComplexKeySampleModelImpl2(MapComplexKeySampleModelImpl2 target);
}

Map Multibinding - qualifier

If you want to configure multiple Map Multibinding of the same type, you can use @Qualifier(javax.inject.Qualifier) annotations like this:

enum class SampleKey {
    KEY1, KEY2, KEY3, KEY4, DEFAULT
}

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class QualifiedSampleMapCustomKey(val key: SampleKey)

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleMapQualifierA

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class SampleMapQualifierB

interface QualifiedMapCustomKeySampleModel {
    fun printTestString()
}

@HiltMapBinds
@QualifiedSampleMapCustomKey(SampleKey.KEY1)
@SampleMapQualifierA
class QualifiedMapCustomKeySampleModelImpl1 @Inject constructor(
    private val testString: String
) : QualifiedMapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifiedMapCustomKeySampleModelImpl1 class.")
    }
}

@HiltMapBinds
@QualifiedSampleMapCustomKey(SampleKey.KEY2)
@SampleMapQualifierB
class QualifiedMapCustomKeySampleModelImpl2 @Inject constructor(
    private val testString: String
) : QualifiedMapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in QualifiedMapCustomKeySampleModelImpl2 class.")
    }
}

// This is the code to get Map Multibinding - qualifier.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    @SampleMapQualifierA
    lateinit var qualifiedCustomKeySampleMapA: @JvmSuppressWildcards Map<SampleKey, Provider<QualifiedMapCustomKeySampleModel>>

    @Inject
    @SampleMapQualifierB
    lateinit var qualifiedCustomKeySampleMapB: @JvmSuppressWildcards Map<SampleKey, Provider<QualifiedMapCustomKeySampleModel>>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        for ((k, v) in qualifiedCustomKeySampleMapA) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }

        for ((k, v) in qualifiedCustomKeySampleMapB) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedMapCustomKeySampleModelImpl1_BindsModule {
  @Binds
  @IntoMap
  @QualifiedSampleMapCustomKey(key = SampleKey.KEY1)
  @SampleMapQualifierA
  public abstract QualifiedMapCustomKeySampleModel bindQualifiedMapCustomKeySampleModelImpl1(QualifiedMapCustomKeySampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class QualifiedMapCustomKeySampleModelImpl2_BindsModule {
  @Binds
  @IntoMap
  @QualifiedSampleMapCustomKey(key = SampleKey.KEY2)
  @SampleMapQualifierB
  public abstract QualifiedMapCustomKeySampleModel bindQualifiedMapCustomKeySampleModelImpl2(QualifiedMapCustomKeySampleModelImpl2 target);
}

Map Multibinding - named

If you want to configure multiple Map Multibinding of the same type, you can use @Named(javax.inject.Named) annotations like this:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class NamedSampleMapCustomKey(val key: SampleKey)

interface NamedMapCustomKeySampleModel {
    fun printTestString()
}

@HiltMapBinds
@NamedSampleMapCustomKey(SampleKey.KEY1)
@Named("sampleNamedMapA")
class NamedMapCustomKeySampleModelImpl1 @Inject constructor(
    private val testString: String
) : NamedMapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedMapCustomKeySampleModelImpl1 class.")
    }
}

@HiltMapBinds
@NamedSampleMapCustomKey(SampleKey.KEY2)
@Named("sampleNamedMapB")
class NamedMapCustomKeySampleModelImpl2 @Inject constructor(
    private val testString: String
) : NamedMapCustomKeySampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NamedMapCustomKeySampleModelImpl2 class.")
    }
}

// This is the code to get Map Multibinding - named.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    @Named("sampleNamedMapA")
    lateinit var namedCustomKeySampleMapA: @JvmSuppressWildcards Map<SampleKey, Provider<NamedMapCustomKeySampleModel>>

    @Inject
    @Named("sampleNamedMapB")
    lateinit var namedCustomKeySampleMapB: @JvmSuppressWildcards Map<SampleKey, Provider<NamedMapCustomKeySampleModel>>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        for ((k, v) in namedCustomKeySampleMapA) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }

        for ((k, v) in namedCustomKeySampleMapB) {
            Log.d("Test!!", "key: $k")
            v.get().printTestString()
        }
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NamedMapCustomKeySampleModelImpl1_BindsModule {
  @Binds
  @IntoMap
  @NamedSampleMapCustomKey(key = SampleKey.KEY1)
  @Named("sampleNamedMapA")
  public abstract NamedMapCustomKeySampleModel bindNamedMapCustomKeySampleModelImpl1(NamedMapCustomKeySampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class NamedMapCustomKeySampleModelImpl2_BindsModule {
  @Binds
  @IntoMap
  @NamedSampleMapCustomKey(key = SampleKey.KEY2)
  @Named("sampleNamedMapB")
  public abstract NamedMapCustomKeySampleModel bindNamedMapCustomKeySampleModelImpl2(NamedMapCustomKeySampleModelImpl2 target);
}



# Supported

Generic Type - single

You can set the return type to a single generic type through @HiltBinds.

interface SingleGenericSampleModel<T> {
    fun printTestString(data: T)
}

@HiltBinds
class SingleGenericSampleModelImpl1 @Inject constructor(
    private val testString: String
) : SingleGenericSampleModel<Int> {

    override fun printTestString(data: Int) {
        Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl1 class. :: Generic type is <Int>")
    }
}

@HiltBinds
class SingleGenericSampleModelImpl2 @Inject constructor(
    private val testString: String
) : SingleGenericSampleModel<String> {

    override fun printTestString(model: String) {
        Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl2 class. :: Generic type is <String>")
    }
}

@HiltBinds
class SingleGenericSampleModelImpl3 @Inject constructor(
    private val testString: String
) : SingleGenericSampleModel<Any> {

    override fun printTestString(data: Any) {
        Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl3 class. :: Generic type is <Any>")
    }
}

// This is the code to get Generic Type - single.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var singleGenericSampleModel1: SingleGenericSampleModel<Int>

    @Inject
    lateinit var singleGenericSampleModel2: SingleGenericSampleModel<String>

    @Inject
    lateinit var singleGenericSampleModel3: SingleGenericSampleModel<Any>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        singleGenericSampleModel1.printTestString(1205)
        singleGenericSampleModel2.printTestString("String")
        singleGenericSampleModel3.printTestString(1205.97)
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl1_BindsModule {
  @Binds
  public abstract SingleGenericSampleModel<Integer> bindSingleGenericSampleModelImpl1(SingleGenericSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl2_BindsModule {
  @Binds
  public abstract SingleGenericSampleModel<String> bindSingleGenericSampleModelImpl2(SingleGenericSampleModelImpl2 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SingleGenericSampleModelImpl3_BindsModule {
  @Binds
  public abstract SingleGenericSampleModel<Object> bindSingleGenericSampleModelImpl3(SingleGenericSampleModelImpl3 target);
}

Generic Type - multiple

You can set the return type to multiple generic types through @HiltBinds.

interface MultipleGenericSampleModel<T1, T2> {
    fun printTestString(data1: T1, data2: T2)
}

@HiltBinds
class MultipleGenericSampleModelImpl @Inject constructor(
    private val testString: String
) : MultipleGenericSampleModel<Int, Any> {

    override fun printTestString(data1: Int, data2: Any) {
        Log.d("Test!!", "TestString is `$testString` in GenericSampleModelImpl1 class. :: Generic type is <Int, Any>")
    }
}

// This is the code to get Generic Type - multiple.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var multipleGenericSampleModel: MultipleGenericSampleModel<Int, Any>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        multipleGenericSampleModel.printTestString(97, 1205)
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class MultipleGenericSampleModelImpl_BindsModule {
  @Binds
  public abstract MultipleGenericSampleModel<Integer, Object> bindMultipleGenericSampleModelImpl(MultipleGenericSampleModelImpl target);
}

Generic Type - set multibinding

You can set the return type as a generic type through @HiltSetBinds. Of course, multiple generic types are possible.

interface SetGenericSampleModel<T> {
    fun printTestString(data: T)
}

@HiltSetBinds
class SetGenericSampleModelImpl1 @Inject constructor(
  private val testString: String
) : SetGenericSampleModel<Int> {

  override fun printTestString(data: Int) {
    Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl1 class. :: Generic type is <Int>")
  }
}

@HiltSetBinds
class SetGenericSampleModelImpl2 @Inject constructor(
  private val testString: String
) : SetGenericSampleModel<Int> {

  override fun printTestString(data: Int) {
    Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl2 class. :: Generic type is <Int>")
  }
}

@HiltSetBinds
class SetGenericSampleModelImpl3 @Inject constructor(
  private val testString: String
) : SetGenericSampleModel<String> {

  override fun printTestString(data: String) {
    Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl3 class. :: Generic type is <String>")
  }
}

@HiltSetBinds
class SetGenericSampleModelImpl4 @Inject constructor(
  private val testString: String
) : SetGenericSampleModel<String> {

  override fun printTestString(data: String) {
    Log.d("Test!!", "TestString is `$testString` in SetGenericSampleModelImpl4 class. :: Generic type is <String>")
  }
}

// This is the code to get Generic Type - Set Multibinding.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

  @Inject
  lateinit var setGenericSampleModelA: @JvmSuppressWildcards Set<SetGenericSampleModel<Int>>

  @Inject
  lateinit var setGenericSampleModelB: @JvmSuppressWildcards Set<SetGenericSampleModel<String>>

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setGenericSampleModelA.forEach {
      it.printTestString(1)
    }

    setGenericSampleModelB.forEach {
      it.printTestString("String1")
    }
  }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl1_BindsModule {
  @Binds
  @IntoSet
  public abstract SetGenericSampleModel<Integer> bindSetGenericSampleModelImpl1(SetGenericSampleModelImpl1 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl2_BindsModule {
  @Binds
  @IntoSet
  public abstract SetGenericSampleModel<Integer> bindSetGenericSampleModelImpl2(SetGenericSampleModelImpl2 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl3_BindsModule {
  @Binds
  @IntoSet
  public abstract SetGenericSampleModel<String> bindSetGenericSampleModelImpl3(SetGenericSampleModelImpl3 target);
}

@Module
@InstallIn(SingletonComponent.class)
abstract class SetGenericSampleModelImpl4_BindsModule {
  @Binds
  @IntoSet
  public abstract SetGenericSampleModel<String> bindSetGenericSampleModelImpl4(SetGenericSampleModelImpl4 target);
}

Nested Type

It also supports nested class as below code.

interface NestedSampleModel {
    interface SampleModel {
        fun printTestString()
    }
}

@HiltBinds
class NestedSampleModelImpl @Inject constructor(
    private val testString: String
) : NestedSampleModel.SampleModel {

    override fun printTestString() {
        Log.d("Test!!", "TestString is `$testString` in NestedSampleModelImpl class.")
    }
}

// This is the code to get Nested Type.
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var nestedSampleModel: NestedSampleModel.SampleModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        nestedSampleModel.printTestString()
    }
}
// generated code
@Module
@InstallIn(SingletonComponent.class)
abstract class NestedSampleModelImpl_BindsModule {
  @Binds
  public abstract SampleModel bindNestedSampleModelImpl(NestedSampleModelImpl target);
}



# Performance monitoring

You can monitor the elapsed time during annotation processing in the Build > Build Output tab of Android Studio.



# License

Copyright 2022 ParkSM

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
Releases(release/1.4.1)
Owner
SangMin Park
Hello, world!
SangMin Park
A ksp library to automatically generate navigation functions for jetpack compose.

Compose/Navigation/Generator ⚠️ This library is still under development and not considered stable! Content Introduction Usage Example: Single destinat

Steffen Eckardt 4 Sep 13, 2022
Kotlin compiler plugin for converting suspend functions to platform-compatible functions

Kotlin suspend transform compiler plugin Summary Kotlin compiler plugin for generating platform-compatible functions for suspend functions. JVM class

ForteScarlet 2 Sep 27, 2022
AppToDo is a simple Android project that performs the basic database CRUD operations that creates a todo task list

AppToDo is a simple Android project that performs the basic database CRUD operations that creates a todo task list

Rafiul Hye 1 Mar 13, 2022
Kotlin Symbol Processor library to create Mutable and Immutable variants of objects.

implier Kotlin Symbol Processor plugin to create Mutable and Immutable variants of objects. Examples @ImmutableImpl @MutableImpl public interface Samp

Vadim Yaroschuk 13 Jul 27, 2022
🎲 Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin.

SealedX ?? Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin. Why SealedX? SealedX generates ext

Jaewoong Eum 209 Oct 2, 2022
Event State Processor Generator plugin is compatible with IntelliJ and Android Studio.

Event State Processor Generator plugin is compatible with IntelliJ and Android Studio. It provides source code generation for the EventStateProcessor Library to increase code productivity in Flutter apps development.

Extreme Vietnam Public 2 Dec 7, 2021
glide's ksp compiler ,use kotlin symbol processor

glide-ksp glide's ksp compiler ,use kotlin symbol processor requirements library version kotlin >= 1.6.10 ksp 1.6.10-1.0.2 usage add jitpack repositor

Mistletoe 23 Jul 6, 2022
Kafka Streams Processor to unwrap CØSMOS blocks into CØSMOS transactions

Kafka Processor CØSMOS-Block A Kafka Streams Processor to unwrap CØSMOS blocks into CØSMOS transactions. Purpose The Kafka Processor CØSMOS-Block is b

OKP4 – Open Knowledge Protocol For 3 Jun 13, 2022
A Kotlin Symbol Processor to list sealed object instances.

Sealed Object Instances A Kotlin Symbol Processor to list sealed object instances. Usage Let's say you have a similar structure of sealed classes (or

Simon Marquis 12 Sep 25, 2022
This repository contains RabbitMQ Protobuf starters with its usage samples for spring-rabbit and spring-cloud-starter-stream-rabbit modules

This repository contains RabbitMQ Protobuf starters with its usage samples for spring-rabbit and spring-cloud-starter-stream-rabbit modules

Maksim Kostromin 2 Nov 29, 2021
KSP-based library that generates lists from your annotation usages

ListGen, Generate Lists From Functions That Have @Listed Annotations! Welcome to ListGen! ListGen is a KSP-based library that can generate lists (and

Adib Faramarzi 19 Sep 1, 2022
Annotation-based library for registration commands

Cargo commands Annotation-based library for registration commands Examples: Plugin main class: class ExamplePlugin : Plugin() { override fun regis

Evgenij 2 Aug 13, 2022
Exktor - Extension modules for Ktor

Extension modules for Ktor ExKtor - it's set of extension libraries for Ktor tha

Simone Stefani 0 Sep 11, 2022
Reia is the Redis Pubsub client that Manase uses to communicate with other modules or nodes.

from Mana Reia is a simple wrapper around Lettuce to enable easy usage of its Redis Pubsub client. This library is only intended to be used for sendin

Mana 1 Apr 29, 2022
Automatically filled the declared non-null field is missing or null with default value.

[TOC] Moshi-kotlin-nullsafe 中文版 1. moshi-kotlin moshi-kotlin support kotlin type safe check. When parsing json, fields declared as non-null types may

null 3 Aug 23, 2022
Attend HoYoLAB Check-in events automatically

Croissant Attend HoYoLAB Check-in events automatically https://play.google.com/store/apps/details?id=com.joeloewi.croissant Stacks Room Database Hilt

joeloewi 3 Jun 14, 2022
Automatically generates UI demos which allow users to call any function with any parameters

Automatically generates UI demos which allow users to call any function (including composable ones) with any parameters. Useful for building demo screens in playground apps of various design systems.

Anton Popov 3 Jul 28, 2022
Kotlin extensions, BindingAdapters, Composable functions for Android CameraX

Setup dependencies { implementation "com.github.skgmn:cameraxx:0.6.0" } Features CameraXX provides extensions methods for CameraX to use functions

null 12 Aug 9, 2022
Helper functions for making Approvals-Java more Kotlin friendly

Approvals-Kt Helper functions for making Approvals-Java more Kotlin-friendly Usage Verify using Approvals-Kt import com.github.greghynds.approvals.Kot

Greg Hynds 2 Oct 18, 2021