I have a few classes that from my client code I need to call. Right now I have a list and then the client has to check the list for the implementation. It is a mess to use.
object Adapter {
val list = listOf(
A(),
B()
...
)
}
Ideally I would only keep one implementation in memory, but sometimes I need to change between implementations.
Make all your classes implement an interface. Since they all implement the same interface you can then assign the concrete instances to the same variable.
interface IWorker {
fun doWork()
}
Then in the Adapter class you can set which implementation you want to use.
object Adapter {
var worker: IWorker = Default()
}
Default represents any of your classes.
class Default: IWorker {
override fun doWork() {}
}
Related
I'm developing a huge section of my Android app in Jetpack Compose with the MVVM pattern.
I have a ViewModel father that is extended by all the other ViewModels. There, I have defined an open function which contains the initialization logic of each ViewModel that I need to call every time I enter in a new screen and to call again when something went wrong and the user clicks on the "try again" button.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
fun onTryAgainClick() {
myInitMethod()
}
}
class MyScreen1ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
class MyScreen2ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
Is there a way I can call this method in the init function of MyFatherViewModel instead of doing it in all the children ViewModels? If I try to do that, it gives me the "Calling non-final function in constructor" warning and, of course, it doesn't work.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
init {
myInitMethod()
}
fun onTryAgainClick() {
myInitMethod()
}
}
Is it possible to call a non-final function in constructor?
Technically yes, but you shouldn't. Kotlin is trying to protect you from problems here. If you call an open function from a constructor, it means you are running code from the child class before the parent class is completely initialized, and before the child class even started initializing. If the child implementation of the open function tries to access properties from the child class, unexpected things may happen. For instance, non-nullable properties could yield null (because not initialized yet), or primitive values could yield their type's default instead of the default value from their initializer:
fun main() {
Child()
}
open class Parent {
init {
initialize()
}
val id = 42
open fun initialize() = println("parent init")
}
class Child : Parent() {
val name = "Bob"
override fun initialize() = println("initializing $name, parent id=$id")
}
This prints the following output:
initializing null, parent id=0
I guess you can see why this is dangerous.
Maybe you should reconsider what you're trying to do with this try-again feature. Maybe a new view model should be instantiated instead (if try-again is to handle crashes, the state of the current view model may actually be bad enough to want to re-create it from scratch anyway).
I have the following code which i think is valid, because the recursion happens as a result of a callback. It's not called directly as a result of the function call. But the compiler seems to think there is a recursion issue
class Model(callBack: CallBack) {
interface CallBack {
fun onSomething()
}
}
class SomeClass {
fun createModel() = Model(callBack)
val callBack = object : Model.CallBack {
override fun onSomething() {
val anotherModel = createModel()
// Use model for something
}
}
}
Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
Is there a workaround for this?
EDIT
I also tried changing callBack to a function so that the same instance is not referenced by multiple models, but I get the same error
The recursive problem mentioned is not about function calls, it's about the compiler trying to find out the types of the declaration and it has stuck in a recursive type checking. It wants to find the output type of createModel which depends on the type of val callback and it depends on createModel again. As it says, declare their types to fix the issue.
class Model(callBack: CallBack)
{
interface CallBack {
fun onSomething()
}
}
class SomeClass {
fun createModel() : Model = Model(callBack)
val callBack : Model.CallBack = object : Model.CallBack {
override fun onSomething() {
val anotherModel : Model = createModel()
// Use model for something
}
}
}
So I am writing this app with remote data source. And I wanted to add local db storage capabilities. I have setup an architecture whereby I have an interface DataSource. A RemoteDataSource and a LocalDataSource classes implement that interface. The RemoteDataSource is injected with ApiInterface retrofit and the LocalDataSource is injected with DAOs.
Now there is this repository interface and implementation SomeDataRepository and SomeDataRepositoryImpl. If I want the repository to be able to fetch the data from api and save it to the database, how do I go about doing that?
Currently I have injected both RemoteDataSource and LocalDataSource classes to the SomeDataRepositoryImpl to access methods from different data sourcces. This way I can call something like localDataSource.saveToDb() and/or remoteDatSource.fetchSomeData() int SomeRepositoryImpl class. But I do not know if passing concrete implementations to a class is the way to go.
But if I pass lets say a single DataSource interface to the SomeDataRepository, I will have to define a saveToDb() function int the interface DataSource and then I will have to implement that in RemoteDataSource as well which is not that good.
Can anyone please guide me through what the best approach is to this solution.
And also while I am at it, is it any good to wrap the data with LiveData wrapper class right in the api interface for retrofit? cause I dont think when a method is called on the repository, I would want to observe it right there in the repo and then access the data to put it onto local db.
Since you want to have the local data source act as a fallback for the remote data source, you can create another data source implementation that is a composition of the local and remote data sources. This composite data source can contain the fallback logic and handle the delegation to the remote and local datasources as needed. Once you have done this, it is a simple matter to create a Koin module to construct these, and bind the composite data source to the data source interface.
Suppose this is your interface and the two data sources you already have:
interface DataSource {
fun getData(): Data
}
class RemoteDataSource : DataSource {
// ...
}
class LocalDataSource : DataSource {
// ...
}
Then you can create a third implementation like this one:
class CompositeDataSource(
val remote: RemoteDataSource,
val local: LocalDataSource
) : DataSource {
override fun getData() : Data {
return try {
remote.getData()
} catch (e: Exception) {
local.getData()
}
}
}
To define all of this, your koin module would look something like this
module {
single { RemoteDataSource() }
single { LocalDataSource() }
single<DataSource> { CompositeDataSource(remote = get(), local = get()) }
}
Edit: If what you actually want is a cache, you can use the local data source as your cache like this:
class CompositeDataSource(
val remote: RemoteDataSource,
val local: LocalDataSource
) : DataSource {
override fun getData() : Data {
return try {
remote.getData().also { local.saveData(it) }
} catch (e: Exception) {
local.getData()
}
}
}
You can try the next approch, it requirs minimal changes and it works for me:
Add interfaces for the remote and local data source, it should inherit the main DataSource interface
interface QuotesDataSource {
fun getQuotes(skip: Int = 0, force: Boolean = false): Flow<List<Quote>>
suspend fun updateQuotes(quotes: List<Quote>)
}
interface QuotesRemoteDataSource : QuotesDataSource
interface QuotesLocalDataSource : QuotesDataSource
Then use those interfaces to create koin module
val repoModule = module {
single { QuotesApi() }
single<QuotesLocalDataSource> { QuotesDatabase(get()) }
single<QuotesRemoteDataSource> { QuotesRemote(get()) }
single<QuotesDataSource> {
QuotesRepository(
local = get<QuotesLocalDataSource>(),
remote = get<QuotesRemoteDataSource>()
)
}
}
I have an interface GalleryImagesDataCallback which I use to pass data from background thread to UI thread, to avoid calling runOnUiThread() from each overriden method of GalleryImagesDataCallback, I have used kotlin higher order function.
interface GalleryImagesDataCallback {
fun fetchedList(list: ArrayList<ImageGalleryItemModel>)
#JvmDefault
fun callMethodOnUIThreadForFetch(mContext: Context, list:ArrayList<ImageGalleryItemModel>,func: (ArrayList<ImageGalleryItemModel>) -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForFetch Enter")
func(list)
}
}
fun deleteList()
#JvmDefault
fun callMethodOnUIThreadForDelete(mContext: Context, func: () -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForDelete Enter")
func()
}
}
}
Calling from background thread:
callback.callMethodOnUIThreadForFetch(mContext,list) {list:ArrayList<ImageGalleryItemModel> -> callback.fetchedList(list)} // callback is reference of GalleryImagesDataCallback
callback.callMethodOnUIThreadForDelete(mContext) {callback.deleteList()}
Problem :
Right now I'm having 2 separate methods callMethodOnUIThreadForDelete() and callMethodOnUIThreadForFetch(). Is there any way in kotlin to create one generic method (say callMethodOnUIThread()) which I can
use to call deleteList() and fetchedList() both with no change in function definition?
First to answer your literal question, your callMethodOnUIThreadForFetch function has unnecessary redirection of the list argument. Why make the list an argument of the higher-order function only to pass it right back to the function argument? You could use your callMethodOnUIThreadForDelete function for either purpose, but suppose we rename it and remove the unsafe cast to Activity by using a Handler:
// In interface:
fun callMethodOnUIThread(context: Context, func: () -> Unit) {
Handler(context.mainLooper).post(func)
}
// From background thread:
callback.callMethodOnUIThread(mContext) { callback.fetchedList(list) }
callback.callMethodOnUIThread(mContext) { callback.deleteList() }
Assuming that you want to simplify the work of implementing this interface, then I don't think this actually helps. You've pushed the work of calling code on the UI thread from the interface implementation into the user of the interface. You may as well create a global helper function, instead of cluttering your interface, which is a weird place for it. Usage becomes more straightforward:
// Global utility function, not in a class
fun Context.onUiThread(func: () -> Unit) {
Handler(mainLooper).post(func)
}
// Usage:
mContext.onUiThread { callback.fetchedList(list) }
mContext.onUiThread { callback.deleteList() }
If you really want to fully encapsulate the thread switching, you would have to change your interface into an abstract class like this:
abstract class GalleryImagesDataCallback {
protected abstract fun fetchedListImpl(list: List<String>)
protected abstract fun deleteListImpl()
fun fetchedList(context: Context, list: List<String>) {
Handler(context.mainLooper).post { fetchListImpl(list) }
}
fun deleteList(context: Context) {
Handler(context.mainLooper).post { deleteListImpl() }
}
}
So instead of implementing the interface, you would create subclasses of this and implement the two abstract functions. Neither the subclass or the user of the callback has to be concerned with ensuring code is called on the UI thread.
I want to pass 2 different object types to a method to update the view. How can I have this method
accept 2 different object types and access them instead of having 2 different methods for 2 different object types.
I needed something like this -
fun updateView(object: Any<T>) {
//Access the objects here to update the view
}
fun <T : Any> updateView(obj: T) {
//Access the objects here to update the view
}
OR
fun updateView(obj: Any ?= null, obj2:Any ?= null) {
// Access the objects here to update the view
// pass check nullity and use which you want (or not null), other parameter will remain null
obj?.let {
it...
}
obj2?.let {
it...
}
}
Call
updateView(obj1, obj2)
// OR
updateView(obj2 = myObj2)
You can use interfaces for this:
interface ViewInterface {
fun action()
}
class ObjectA : ViewInterface {...}
class ObjectB : ViewInterface {...}
fun updateView(ob: ViewInterface) {
ob.action()
}
try something like this
fun updateView(variable1: Any? = null, variable2:Any? = null) {
//Access the objects here to update the view
}
using named parameters, you can then just set the variables you need when calling the method:
updateView(variable1 = "something")
updateView(variable2 = "something else")
Have your 2 objects implement the same interface or inherit from the same superclass then do something like:
fun updateView(object: MyInterface) {
...
}
Use polymorphism
fun updateView(object: X) {
...
}
fun updateView(object: Y) {
...
}
You can pass two types of object by this way
fun updateView(data:Any? = null,data2:Any?=null) {
//Cast Your Object To your desired type and also can pass null too
// Access the objects here to update the view
}
I would advise separating the two functions, or to use inheritance of some sort to use a single function. But as I see none of those above (which are correct, from the SOLID point of view) satisfies your request, you can just check inside the function based on class.
fun updateView(object: Any) {
when(object){
is Class1Type -> // do whatever fits for the first case
is Class2Type -> // do whatever fits for the second case
else -> // etc.
}
}
The best solution is not related to Kotlin at all. Just make both of them implement an interface and use this interface as the function parameter type.
In general, accepting Any as an input type is not a good practice and using generics is an overkill.
interface DoesStuff
class DoesStuffA: DoesStuff { }
class DoesStuffB: DoesStuff { }
fun doStuff(doer: DoesStuff) {
// do stuff
// if need to distinguish between types
when (doer) {
is DoesStuffA -> // do A
is DoesStuffB -> // do B
}
}