I am trying to build a set of providers for realm objects.
Here is an example structure I've tried to build:
Interface:
interface IDataProvider<out T : RealmObject> {
fun getRealmObject(): T
}
Base provider class with companion function for typed provider instantiation:
open abstract class BaseProvider<out T : RealmObject> constructor(protected val context: Context?) : IDataProvider<T> {
companion object {
fun <T : RealmObject, E : BaseProvider<T>> create(context: Context?): E {
if (something) {
return SomeChildProviderProvider(context)
} else {
throw TypeNotSupportedException()
}
}
}
}
And here is a child class:
class SomeChildProvider(context: Context?) : BaseProvider<ChildRealmModel>(context){
override fun getRealmObject(): ChildRealmModel {
throw UnsupportedOperationException("not implemented")
}
}
Problem I have is on the line
return SomeChildProviderProvider(context)
Type mismatch.
Required: E.
Found: SomeChildProvider.
I can't figure out why it does not see that E is actually SomeChildProvider.
Thank you.
P.S. I know that I can cast it to E, but in my opinion, it should not be needed in this situation. Maybe I am missing something obvious here or probably lack of Kotlin knowledge.
UPDATE1:
After the first answer, we have realized that code above does not make much sense since we have to define a type of returning provider and to pass it into create method. Initial idea was that create method returns some type which is BaseProvider subtype. Here are the changes I have made in order to support the initial idea:
IDataProvider
interface IDataProvider {
fun execute(realm: Realm)
fun createModel(realm: Realm): RealmObject
}
BaseProvider
open abstract class BaseProvider constructor(protected val context: Context?) : IDataProvider {
override fun execute(realm: Realm) {
realm.executeTransaction { r ->
createModel(r)
}
}
companion object {
fun create(context: Context?): IDataProvider {
if (something) {
return ChildProvider(context)
} else {
throw TypeNotSupportedException()
}
}
}
}
ChildProvider
class ChildProvider(context: Context?) : BaseProvider(context) {
override fun createModel(realm: Realm): ChildRealmModel {
var realmObject = realm.createObject(ChildRealmModel ::class.java)
//object property initialization
return realmObject
}
}
UI call
BaseProvider.create(context).execute(realm)
Although, createModel method returns RealmObject, it's instance will be of ChildRealmModel. What I don't like about it is that we have to inspect instance type and cast into if we need exact model somewhere else.
Your code is not consistent.
In the function declaration you pledge to return E, which is a subtype of BaseProvider<T> and can be chosen by the user on the call site.
But in the implementation you return SomeChildProviderProvider, which is of course a subtype of BaseProvider<T>, but still can be totally unrelated to E which was chosen by the user.
An example:
class AnotherChildProvider : BaseProvider<ChildRealmModel>(context) {...}
val x = BaseProvider.create<ChildRealmModel, AnotherChildProvider>(context)
What is the type of x? According to the function signature, it must be AnotherChildProvider. But inside the function you return SomeChildProviderProvider, which CAN NOT be casted to AnotherChildProviderProvider.
Related
I want to shadow a Kotlin companion object. The companion object what i want to shadowing is:
class MyLogClass {
companion object {
#JvmStatic
fun logV(tag: String, messageProvider: () -> String) {
if (SPUtils.getLogLevel() >= mLogLevel) {
Log.v(tag, messageProvider.invoke())
}
}
}
}
What I have tried:
// Shadow class...
#Implements(MyLogClass.Companion::class)
class ShadowMyLogClass {
#Implementation
fun v(tag: String, messageProvider: () -> String) {
redirectConsole(tag, messageProvider)
}
private fun redirectConsole(tag: String, messageProvider: () -> String) {
println("[$tag]${messageProvider.invoke()}")
}
}
// And in Testing class...
class TestMyLogClass {
#Test
fun test() {
MyLogClass.logV("some tag") {
"some message"
}
}
}
But what I have tried occur an Error:
Caused by: java.lang.IllegalAccessError: tried to access class kotlin.jvm.internal.DefaultConstructorMarker from class com.example.zspirytus.log.impl.MyLogClass$Companion
It seems that there is lost an constructor method which type is DefaultConstructorMarker, How can I make an DefaultConstructorMarker or other way to create a Shadow MyLogClass? Thanks for your reading and answers!
Here's what I did to shadow a method in the companion object
#Implements(Object::class)
class ShadowObject {
companion object {
#Implementation
#JvmStatic
fun verify(): Boolean {
return true
}
}
}
Use:
#RunWith(AndroidJUnit4::class)
#Config(shadows = [
ShadowObject::class
])
class UserTest {
// Rest of testing class
}
In your case, I'd say you just need to wrap your #Implementation method with companion object, and change #Implements(MyLogClass.Companion::class) to just #Implements(MyLogClass::class)
I have a simple BottomSheetDialogFragment:
class MyBottomSheetDialog : BottomSheetDialogFragment() {
companion object {
private const val SOME_KEY = "some_key"
fun newInstance(something: Boolean): MyBottomSheetDialog {
return MyBottomSheetDialog().apply {
arguments = bundleOf(SOME_KEY to something)
}
}
}
...
...
}
Which I then display with:
MyBottomSheetDialog.newInstance(false).show(childFragmentManager, "my_dialog")
What is the advantage of this typical approach to just using a constructor parameter and displaying the dialog like this:
class MyBottomSheetDialog(private val something: Boolean) : BottomSheetDialogFragment() {
...
...
}
MyBottomSheetDialog(false).show(childFragmentManager, "my_dialog")
Simple thing is if your class undergoes for a configuration changes then system looks for default constructor to recreate the class i.e without parameters at that you will get Exception.
The following Code A is from the project architecture-samples at https://github.com/android/architecture-samples
The function getTasks() will return Result<List<Task>>.
The class data class Error(val exception: Exception) will return Result<Nothing>().
I think the code Error(e) will cause error because it can't return Result<List<Task>>.
Is the Nothing child class of any other class in Kotlin?
Code A
class TasksLocalDataSource internal constructor(
private val tasksDao: TasksDao,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksDataSource {
...
override suspend fun getTasks(): Result<List<Task>> {
return withContext(ioDispatcher) {
try {
Success(tasksDao.getTasks())
} catch (e: Exception) {
Error(e) //I think that it will be cause error
}
}
}
...
}
interface TasksDao {
...
#Query("SELECT * FROM Tasks")
suspend fun getTasks(): List<Task>
...
}
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
Loading -> "Loading"
}
}
}
By "child" class, I'm assuming you mean subtype? If so, yes Nothing is indeed a subtype of every other type in Kotlin. Its the opposite analog of Any? which is the supertype of every type in Kotlin.
Nothing is what allows functions like TODO() to work. The implementation of this function is:
public inline fun TODO(): Nothing = throw NotImplementedError()
indicating that TODO() never returns anything i.e. it always throws an exception at runtime. This is what allows TODO() to be placed into any context. For example:
fun foo(): String = TODO()
compiles without any error even though TODO() does not return a String , because the compiler knows TODO() will never return anything (more specifically, Nothing is a subtype of String and therefore "returns" a valid type in that expression).
Now, getting to your question:
I think the code Error(e) will cause error because it can't return Result<List<Task>>.
We noted that Nothing is indeed a subtype of every other type.
We must also note that the definition of the sealed Result class i.e. Result<out R> uses the variance annotation out. This means that the type parameter T of Result is always only returned from Result, and never consumed by Result i.e. Result is covariant in T. Quoting the Kotlin documentation:
The general rule is: when a type parameter T of a class C is declared out, it may occur only in out-position in the members of C, but in return C<Base> can safely be a supertype of C<Derived>.
Combining this rule with the knowledge that Nothing is a subtype of every other type, this allows Error(e) which implements Result<Nothing> to be a valid return value of Result<List<Task>>, and therefore there is no error in the line of code you indicated.
I'm new at Koin. I have set all the stuff and is working. But I'm getting some problems when I'm trying to inject interactor and presenter at the same time. That not sure it is possible.
This is my Module
val applicationModule = module(override = true) {
factory{VoucherImpl(get())}
factory<VoucherContract.Presenter> { (view: VoucherContract.View) -> VoucherPresenter(view, get()) }
}
This is my Activity where inject the presenter
private val presenter: VoucherContract.Presenter by inject { parametersOf(this)}
This is my Presenter
class VoucherPresenter (private var view: VoucherContract.View?, private var mCodeRechargeInteract : VoucherImpl) : VoucherContract.Presenter, VoucherContract.Callback, KoinComponent {
override fun create() {
view?.initView()
view?.showProgress()
mCodeRechargeInteract.run()
}
.
.
.
Interactor class
class VoucherImpl(private var mCallback: VoucherContract.Callback?) : AbstractInteractor() {
.
.
.
contract
interface VoucherContract {
interface Presenter {
fun create()
fun destroy()
fun checkIfShoppingCartHaveItems()
fun addVoucherToShoppingCart(voucherProduct: Product)
fun onItemClick(product: Product)
}
interface Callback {
fun onResponseVouchers(vouchers: List<Product>?)
fun onError()
}
}
With this code I get
No definition found for 'xxx.voucher.VoucherContract$Callback' has been found. Check your module definitions.
Then, I try to put it in the module and I can't do it because I get: a Type mismatch. Required VoucherContract.Callback Found VoucherImpl
factory<VoucherContract.Callback> { (callBack: VoucherContract.Callback) -> VoucherImpl(callBack) }
You have a circular dependency that's why this doesn't work.
VoucherImpl(VoucherContract.Callback) and VoucherPresenter(View, VoucherImpl):VoucherContract.Callback
There are multiple ways out of this predicament.
I would recommend the following changes:
The VoucherImpl should not have the constructor parameter VoucherContract.Callback. This callback should be the parameter of a method something like this:
class VoucherImpl : AbstractInteractor(){
fun listen(VoucherContract.Callback){...}
}
This way the dependency becomes one way and you can inject them.
The error occurs when passing this to onResume.
Somehow it doesn't recognize that this implements ActivityLifecycleType, Am I missing something?
open class BaseActivity<ViewModelType: ActivityViewModel<*>>: RxAppCompatActivity(), ActivityLifecycleType {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this) ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType: ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}
Kotlin's generics' more strict that you have you write use the code below:
open class BaseActivity<ViewModelType : ActivityViewModel<ActivityLifecycleType>> : ActivityLifecycleType, RxAppCompatActivity() {
protected var viewModel: ViewModelType? = null
#CallSuper
override fun onResume() {
super.onResume()
viewModel?.onResume(this#BaseActivity) // ==> Error Required Nothing, Find BaseActivity<ViewModelType>
}
}
open class ActivityViewModel<in ViewType : ActivityLifecycleType> {
fun onResume(view: ViewType) {
// Do something
}
}
interface ActivityLifecycleType {
fun lifecycle(): Observable<ActivityEvent>
}
What I've done is to change the declaration in the first line.
Java is too weak to check the generic type but Kotlin do.
Mention there're two things you have to do next:
implement lifecycle in BaseActivity or make it abstract.
it's recommended to use lateinit var viewModel instead of nullable types