Cannot inject List<String> for class extending Java Class - kotlin

Getting below error while trying to Inject List of String into a class constructor. I am extending AbstractDelegator java class as CancelDelegator. AbstractDelegator is referenced from a dependency jar so I can't change that.
No implementation for java.util.List<java.lang.String> annotated with #com.google.inject.name.Named(value=CANCEL_TASKS) was bound.
while locating java.util.List<java.lang.String> annotated with #com.google.inject.name.Named(value=CANCEL_TASKS)
for parameter 1 at com.cancel.core.delegator.CancelDelegator.<init>(CancelDelegator.kt:18).
at com.cancel.core.config.CancelModule.configure(CancelModule.kt:45)
Following are the classes:
AbstractDelegator.java ( from dependency jar)
public abstract class AbstractDelegator<T extends Request, R extends Response> implements Delegator<T, R> {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected Processor<T, R> processor;
protected List<String> taskSequence;
protected AbstractDelegator(Processor<T, R> processor, List<String> taskSequence) {
this.processor = processor;
this.taskSequence = taskSequence;
}
}
CancelDelegator.kt
#Singleton
class CancelDelegator #Inject constructor(
#Named(CancelConstant.CANCEL_PROCESSOR) processor: Processor<Request?, Response?>?,
#Named(CancelConstant.CANCEL_TASKS) taskSequence: List<String?>?
) : AbstractDelegator<Request?, Response?>(processor, taskSequence)
GuiceModule.kt
bind<Delegator<Request?, Response?>?>(object : TypeLiteral<Delegator<Request?, Response?>?>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_DELEGATOR))
.to(CancelDelegator::class.java)
bind<List<String?>?>(object : TypeLiteral<List<String?>?>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_TASKS))
.toInstance(Arrays.asList(CancelConstant.CANCEL_PENALTY_TASK))
Its only the List that Guice is not able to inject. Other injections are working fine. I have tried changing List<String?>? to List<String?> and List but nothing seems to work.

Like explained in this GitHub issue,
var setA: Set<Runnable>
var setB: MutableSet<Runnable>
is equivalent to the following in Java:
Set<? extends Runnable> setA;
Set<Runnable> setB;
You can fix it with the #JvmSuppressWildcards annotation. Something like the following:
bind(object : TypeLiteral<List<#JvmSuppressWildcards String>>() {})
.annotatedWith(Names.named(CancelConstant.CANCEL_DELEGATOR))
.toInstance(listOf("hello", "goodbye"))

Related

String cannot be provided without an #Provides-annotated method

I'm trying to use ActivityScoped and ActivityComponent in a simple project, but i was getting below error
#javax.inject.Named("String2") java.lang.String cannot be provided without an
#Provides-annotated method.
public abstract static class SingletonC implements BaseApplication_GeneratedInjector,
#javax.inject.Named("String2") java.lang.String is injected at
com.example.hiltoplayground.TestViewModel(testString)
com.example.hiltoplayground.TestViewModel is injected at
com.example.hiltoplayground.TestViewModel_HiltModules.BindsModule.binds(vm)
#dagger.hilt.android.internal.lifecycle.HiltViewModelMap
java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>>
is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.example.hiltoplayground.BaseApplication_HiltComponents.SingletonC → com.example.hiltoplayground.BaseApplication_HiltComponents.ActivityRetainedC → com.example.hiltoplayground.BaseApplication_HiltComponents.ViewModelC]
But if i use SingletonComponent and #Singleton it runs perfect, no issue.
Here is the affected module
#Module
#InstallIn(ActivityComponent::class)
object MainModule {
#ActivityScoped
#Provides
#Named("String2")
fun provideTestString2 (
#ApplicationContext context: Context
) = context.getString(R.string.string_inject)
}
Below is the ViewModel (No problem here i guess)
#HiltViewModel
class TestViewModel #Inject constructor(
#Named("String2") testString: String): ViewModel() {
init{
Log.d("String2Message","Show $testString")
}
}
You can use #ActivityRetainedScoped in place of #ActivityScoped and make #InstallIn(ActivityRetainedComponent::class) from #InstallIn(ActivityComponent::class).

How to mock an SQLiteOpenHelper

I am trying to mock an SQLiteOpenHelper class in instrumented tests so whenever any fragment tries to get information from the database it returns a generic result. However, I keep getting an error saying:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.example.cleaningschedule.helpers.DatabaseHandler Mockito cannot
mock/spy because :
final class
at com.example.cleaningschedule.ToDoListInstrumentedTest.oneTask(ToDoListInstrumentedTest.kt:81)
The test class is:
#RunWith(AndroidJUnit4::class)
class ToDoListInstrumentedTest {
#Rule
#JvmField var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
private fun getActivity() = activityRule.activity
#After
fun tearDown() {
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase("TaskDatabase")
}
#Test
fun oneTask() {
val mock = mock(DatabaseHandler::class.java)
`when`(mock.getTasks()).thenThrow()
onView(withId(R.id.taskName)).check(matches(isDisplayed()))
}
}
The class I am trying to mock is:
class DatabaseHandler(context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
companion object {
private const val DATABASE_VERSION = 5
private const val DATABASE_NAME = "TaskDatabase"
...
}
override fun onCreate(db: SQLiteDatabase?) {
...
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
...
}
fun getTasks(): MutableList<Pair<MutableList<String>, MutableList<Room>>> {
...
}
}
I have looked at several other similar questions but none have helped:
Error mocking Class which hold reference to SQLiteOpenHelper
Mock final class in java using mockito library - I had a lot of issues with import PowerMock
How to mock a final class with mockito - I have added the dependency and created the file with the mock-maker-inline line as suggested in the answers put I still get the same error. I also tried the answer that suggested Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable)) but this gave me a 'Not enough information to infer type variable T' error
Mock final class with Mockito 2
Mockito cannot mock/spy because : Final Class
Cannot mock/spy class java.util.Optional
I will made an Interface :
public interface ContainerHandler {
MutableList<Pair<MutableList<String>, MutableList<Room>>> getTasks();
}
Then I made DatabaseHandler inherit this interface, I call Mockito's mock function with the Interface.
val mock = mock(ContainerHandler::class.java)
`when`(mock.getTasks()).thenThrow()
And finally I inject my mock into the tested class.

Inject Kotlin inline class with Dagger/Hilt

Is there a way to inject instances of a Kotlin inline class with Dagger? In other words is there any way to get this code to compile assuming that we have a Dagger component that includes UserModule?
inline class Username(val name: String)
#Module
class UserModule {
#Provides
fun provideUsername(): Username = Username("default_user")
}
class MyClass #Inject constructor(private val username: Username)
I am specifically trying to do this on Android using Hilt. Not sure if that matters or not.
Same here - wanted this instead of injecting a "Named" or class instance wrapper string via Hilt:
#JvmInline
value class ApplicationFlavor #Inject constructor(private val flavor: String) {
fun isPaid() = flavor.equals("paid", true)
fun isFree() = flavor.equals("free", true)
fun trial() = flavor.equals("trail", true)
}
but decided to settle for using a class instance since I couldn't seem to find a workaround:
class ApplicationFlavor...
#Provides
#Singleton
fun provideApplicationFlavor() = ApplicationFlavor(BuildConfig.FLAVOR)

Not able to inject a multi-binding with kotlin and dagger

I have the following definitions:
#Module
class WeaverDataModule {
// Provide the three pumps from providers
// All of them still explicitly mark 'Pump' as their return type
#Provides #IntoSet fun providesPump(thermosiphon: Thermosiphon) : Pump = thermosiphon
#Provides #IntoSet fun providesAnotherPump(suctionBased: SuctionBased) : Pump = suctionBased
#Provides #IntoSet fun providesGenericPump(genericPump: GenericPump) : Pump = genericPump
}
#Component(modules = [WeaverDataModule::class])
interface WeaverData {
// Get the CoffeeMaker
fun coffeeMaker(): CoffeeMaker
// Get the list of pumps
fun getPumps() : Set<Pump>
}
interface Pump
// The three pumps
class Thermosiphon #Inject constructor(val heater: Heater) : Pump
class SuctionBased #Inject constructor() : Pump
class GenericPump #Inject constructor() : Pump
// Some random heater
class Heater #Inject constructor()
In my code, when I do the following:
val cm = DaggerWeaverData.builder().build().getPumps()
I do get the three pumps as expected. However, when I'm trying to inject it into some other class:
class CoffeeMaker #Inject constructor(
private val heater: Heater,
private val pump: Set<Pump>
) {
fun makeCoffee() =
"Making coffee with heater ${heater::class.java} and using pumps" +
" ${pump.map { it::class.java }.joinToString(",")}"
}
I get the following error:
e: .../WeaverData.java:7: error: [Dagger/MissingBinding] java.util.Set<? extends weaver.Pump> cannot be provided without an #Provides-annotated method.
public abstract interface WeaverData {
^
java.util.Set<? extends weaver.Pump> is injected at
weaver.CoffeeMaker(…, pump)
weaver.CoffeeMaker is provided at
weaver.WeaverData.coffeeMaker()
I've tried injecting Collection<Pump> also, but I still get a similar error. In the dagger docs on multibinding, the example (in Java) shows the following:
class Bar {
#Inject Bar(Set<String> strings) {
assert strings.contains("ABC");
assert strings.contains("DEF");
assert strings.contains("GHI");
}
}
which is exactly what I'm doing. And for constructor-based injection, it is working just fine in Kotlin, because the following compiles and runs as expected:
class CoffeeMaker #Inject constructor(
private val heater: Heater
) {
fun makeCoffee() =
"Making coffee with heater ${heater::class.java}"
}
So I'm kind of at a loss on how do I get this multibinding to work.
So turns out what you need to do is:
class CoffeeMaker #Inject constructor(
private val heater: Heater,
private val pumps: Set<#JvmSuppressWildcards Pump>
) {
fun makeCoffee() =
"Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}
This is because Set is defined in Kotlin as Set<out E> which translates into Java as Set<? extends Pump>. From a type-theory perspective, Set<? extends Pump> is different from Set<Pump> and hence Dagger (probably) refuses to see Set<Pump> as an injectable for Set<? extends Pump>, which is fair and the right behavior.
The problem we have is that for any of these collections, since they are immutable by default, a declaration of type Set<X> will translate to Set<? extends X>, as an immutable collection only has references to the resolved type on returns and is hence covariant. To verify this theory, the following also works:
class CoffeeMaker #Inject constructor(
private val heater: Heater,
private val pumps: MutableSet<Pump>
) {
fun makeCoffee() =
"Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}
Note the use of MutableSet, which is defined as MutableSet<E> : Set<E> .... This is probably not something one should use because I doubt that this set is actually mutable. So what we do need is for the kotlin compiler to treat Set<out E> as Set<E> (the assignabiliy is valid in this case, just not the other way around). So do so, we use the #JvmSuppressWildcards annotation. I hope this helps somebody else facing similar issues.

Provide instance without interface using Dagger

There is way to provide instance without actual constructor calling.
class ModelImpl #Inject constructor(...): Model{}
#Provides
fun model(inst: ModelImpl): Model = inst
Is there way to do the same if there is no interface? Dagger knows already all dependencies for ModelImpl, so it can create an instance.
This gives obviously dependency cycle:
#Provides
fun model(inst: ModelImpl): ModelImpl = inst
When you use constructor injection Dagger can construct the object for you and you're already using Dagger to create ModelImpl to use it as a binding for Model in your example!
class ModelImpl #Inject constructor(...): Model{}
#Provides
fun model(inst: ModelImpl): Model = inst
// somewhere else...
// both variants would work!
#Inject lateinit var modelImpl : ModelImpl
#Inject lateinit var model : Model
The same would work without the interface
class ModelImpl #Inject constructor(...)
// somewhere else...
#Inject lateinit var model : ModelImpl
If you annotate the constructor then Dagger can create the object for you (if all dependencies can be resolved). This works the same wherever you request the object / dependency,
as a parameter in a #Provides annotated method (as your example)
as a field injection property (#Inject lateinit var)
as a parameter in another objects constructor
as a provision method in a component (fun getFoo() : Foo)
All of the following would work
// foo and bar can both be constructor injected
class Foo #Inject constructor()
class BarImpl #Inject constructor(val foo : Foo) : Bar
#Module
interface BarModule() {
#Binds // you should prefer Binds over Provides if you don't need initialization
// barImpl can be constructor injected, so it can be requested/bound to its interface here
fun bindBar(bar : BarImpl) : Bar
}
#Component(modules = BarModule::class)
interface BarComponent {
fun getBar() : Bar // returns barImpl due to binding
}
#Inject lateinit var bar : BarImpl // but we could as well use the implementation directly
#Inject lateinit var bar : Foo // or just foo
I recommend you to start with a small example, then compile the project and have a look at the generated code. If something is wrong you'll get errors immediately, while you can play around and try different setups!
An addition to David Medenjak anwser. If there is no interface and there is no requirement to group instances into modules, then module could be omitted completely:
class Model #Inject constructor(...){
//...
}
#Component
interface SomeComponent{
fun model(): Model
}
val model = someComponent.model()