Pair with generic type in Kotlin - android

I want to create a Pair which should take a generic type. I mean, I can pass a String to Int. How can I achieve that?
Example with the current behavior:
val liveData = MutableLiveData<Pair<Boolean, Int>>()
Expectation:
val liveData = MutableLiveData<Pair<T, T>>()

try this
class Abc<T, U> {
val liveData = MutableLiveData<Pair<T, U>>()
}
fun <T, U> Abc1(): MutableLiveData<Pair<T, U>> {
return MutableLiveData<Pair<T, U>>()
}
val liveData = Abc<String, Int>()

If you want to pass either String or Int, a sealed class may be the right choice, rather than a generic.
In your case something like:
sealed class StrInt
data class Numeric(val value:Int):StrInt()
data class Alphanum(val value:String):StrInt()
val a:Pair<StrInt, StrInt> = Numeric(10) to Alphanum("qwerty")

Related

Serialize class with abstract parameter to JSON

I have 3 classes:
GameResultList which is basically ArrayList with some helper methods in it
GameResult with an abstract value gameMode
GameMode
public class GameResultList extends ArrayList<GameResult> {
...
}
class GameResult(
val gameMode: GameMode,
val score: Int,
timeSpentInSeconds: Int,
val completionDateTime: Date
) {
...
}
GameMode class:
abstract class GameMode(
val suggestionsActivated: Boolean,
val screenOrientation: ScreenOrientation // enum: PORTRAIT, HORIZONTAL
) {
...
}
I need to serialize GameResultList into JSON.
Since the parameter gameMode is abstract, Gson throws an exception. After some research, I decided to give Moshi a try. I have added PolymorphicJsonAdapterFactory and KotlinJsonAdapterFactory, but the result is always empty ({}).
How I set up Moshi:
private val moshi =
Moshi.Builder().add(PolymorphicJsonAdapterFactory.of(GameMode::class.java, "GameMode")
.withSubtype(GameOnTime::class.java, "GameOnTime")
.withSubtype(GameOnCount::class.java, "GameOnCount"))
.add(KotlinJsonAdapterFactory())
.build()
private val jsonAdapter: JsonAdapter<GameResultList> = moshi.adapter(GameResultList::class.java)
This returns empty JSON response:
jsonAdapter.toJson(gameResultList)
So how can I serialize the GameResultList? Is there an easy way? Also, it's not necessary to use Moshi, it can be anything else for the sake of easiness.
After some investigation, I found out that the main problem is that array lists require explicit converters.
class GameResultListToJsonAdapter {
#ToJson
fun arrayListToJson(list: GameResultList): List<GameResult> = list
#FromJson
fun arrayListFromJson(list: List<GameResult>): GameResultList = GameResultList(list)
}
Also, there is a problem with handling the Date type, I have replaced it with Long to not make another explicit converter.

Kotlin: How to change values of MutableLiveData in ViewModel without using getters and setters

Here is my viewmodel:
class MyProfileEditSharedViewModel : ViewModel() {
val question = MutableLiveData<String>()
val answer = MutableLiveData<String>()
fun setQuestion (q: String) {
question.value = q
}
fun setAnswer (a: String) {
answer.value = a
}
}
I set the data using setQuestion and setAnswer like this:
viewModel.setQuestion(currentUserInList.question)
viewModel.setAnswer(currentUserInList.answer)
I try to get question and answer from the ViewModel like this:
val qnaQuestionData = communicationViewModel.question as String
val qnaAnswerData = communicationViewModel.answer as String
Compiler says I cannot cast MutableLiveData to string.
Should I make a separate getter like my setter? I heard that you don't need to use getters and setters in kotlin, is there anyway to edit val question and val answer in my viewmodel without using getters and setters?
Thank you!!
You can't cast it to String because the type of object is MutableLiveData, but you can access the value with .value property
val qnaQuestionData = communicationViewModel.question.value
val qnaAnswerData = communicationViewModel.answer.value
in this case, may facing errors about MutableLiveData initialization.
another way is observing the LiveData for changes:
communicationViewModel.question.observe(this, Observer{ data->
...
})
Or if you have not accessed to any lifecycle owner
communicationViewModel.question.observeForever(Observer{ data->
...
})
but please remember to remove the observer through removeObserver method
for setting the values it's better to use properties directly or binding way
communicationViewModel.question.postValue("some new value")
Or
communicationViewModel.question.value = "some new value"
Suggestion for MutableLiveData properties:
val question: MutableLiveData<String> by lazy { MutableLiveData<String>() }
val answer: MutableLiveData<String> by lazy { MutableLiveData<String>() }
https://developer.android.com/reference/android/arch/lifecycle/LiveData
Create some sort of getter method in your ViewModel
fun getQuestion(): LiveData<String> {
return question //this works because MutableLiveData is a subclass of LiveData
}
Then, you can observe the value in whatever class you care about the value. ie:
communicationsViewModel.getQuestion().observe(this, Observer {
//do something with the value which is 'it'. Maybe qnaQuestionData = it
}
Note if you're trying to observe the value from a fragment or something, you will have to change the parameter this, to viewLifecycleOwner

Kotlin annotation - Require a parameter is a Constant variable from specific class

I have a function filter here
fun filter(category: String) {
...
}
and a Class with many constant string
object Constants {
val CAT_SPORT = "CAT_SPORT"
val CAT_CAR = "CAT_CAR"
...
}
How to ensure the parameter category is a constant string from Constants (or throw warning)?
I am looking for something like #StringRes.
I know Enum may do the trick but prefer not to code refactor at this moment.
Using androidx.annotation you can do something like this:
object Constants {
#Retention(AnnotationRetention.SOURCE)
#StringDef(CAT_SPORT, CAT_CAR)
annotation class Category
const val CAT_SPORT = "CAT_SPORT"
const val CAT_CAR = "CAT_CAR"
}
fun filter(#Constants.Category category: String) {
...
}

How Does Android LiveData get() syntax work?

I understand the need for creating getter and setter points for LiveData in the ViewModel, but I'm looking to understand how the get() syntax works in Android.
ie:
val isRealtime: LiveData<Boolean>
get() = _isRealtime
private val _isRealtime = MutableLiveData<Boolean>()
get() is not related to Android.
val isRealtime: LiveData<Boolean>
get() = _isRealtime
Here, get() is overriding the automatically-generated Kotlin getter function for the isRealtime property. So, instead of returning its own value, it returns the value of _isRealtime.
Personally, I recommend simpler syntax:
private val _isRealtime = MutableLiveData<Boolean>()
val isRealtime: LiveData<Boolean> = _isRealtime
The objective of either of these is to keep the mutability private, so consumers of this class do not accidentally update the MutableLiveData themselves.
In Kotlin we have multiple ways of exposing live data from ViewModel to the view.
class MyViewModel: ViewModel() {
// Solution 1 - make MutableLiveData public
// This approach works, but this is a bad idea because
// view can modify the LiveData values
val liveDataA1 = MutableLiveData<State>()
// Solution 2 - let's make LiveData public (expose it instead of MutableLiveData)
// Now from view perspective this solution looks fine, bu we have a problem,
// because we need MutableLiveData within ViewModel to put/post new values to
// the stream (we can't post values to LiveData).
val liveDataA2 = MutableLiveData<State>() as LiveData<State>
// Let's capture our requirements:
// 1. We need to expose (immutable) LiveData to the view,
// so it cannot edit the data itself.
// 2. We need to access MutableLiveData from ViewModel to put/post new values.
// Now, let's consider few appropriate solutions
// Solution 3
// Let's name mutable live data using underscore prefix
private val _liveData3 = MutableLiveData<State>()
val liveData3 = _liveData3 as LiveData<State>
// Solution 4
// We can also perform casting by specifying type for a variable
// (we can do it because MutableLiveData extends LiveData)
private val _liveData4 = MutableLiveData<State>()
val liveData4: LiveData<State> = _liveData4
// Solution 5
// Starting from Kotlin 1.4-M.2 we can delegate call to another property
private val _liveData5 = MutableLiveData<State>()
val liveData5 by this::_liveData5
// Solution 6
// These above solutions work quite well, but we could do even better by
// defining custom asLiveData extension function.
private val _liveData6 = MutableLiveData<State>()
val liveData6 = _liveData6.asLiveData()
fun <T> MutableLiveData<T>.asLiveData() = this as LiveData<T>
// Amount of code is similar, but notice that this approach works much better
// with code completion.
// Solution 7 (IMO Best)
// We can also use alternative naming convention - use "mutableLiveData"
// as variable for mutable live data instead of using underscore prefix
private val mutableLiveData7 = MutableLiveData<State>()
val liveData7 = mutableLiveData7.asLiveData()
// BTW
// We could also expose getLiveData8() method, but liveData is a state not an action.
// Solution 9
// This does not create backing field for the property
// (more optimised but still Solution 7 is easier to use)
private val _liveData9 = MutableLiveData<State>()
val liveData9 get() = _liveData9 as LiveData<State>
}
I wrote a util function for this logic:
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import kotlin.reflect.KProperty
fun <T> immutable(data: MutableLiveData<T>): Immutable<T> {
return Immutable(data)
}
class Immutable<T>(private val data: MutableLiveData<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): LiveData<T> {
return data
}
}
Then you can use in any of your ViewModel as:
private val _counter: MutableLiveData<Int> = MutableLiveData()
val counter: LiveData<Int> by immutable(_counter)
or in short:
private val _counter = MutableLiveData<Int>()
val counter by immutable(_counter)

How to get value of the ObservableField in android

Hi I have this ObservableField in my java code. I want to get the value of it which can be done by calling get method on it.
val email = ObservableField<String>()
This can be done using below approach. I am confused and don't know should I make a getter here to get the value of it ? or there is different standard approach to get the value of ObservableField I am using RxJava too in my app.
fun login(view: View) {
val emailVal = email.get()
}
This is exactly what delegation is about. Delegation of a property in Kotlin means having a class that implements the operator function getValue and optionally setValue, which will be called when accessing or updating the property.
Your delegate could look like this:
class <T> ObservableDelegate
{
val field = ObservableField<T>()
operator fun getValue(self: Any?, prop: KProperty<*>) : T
= field.get()
operator fun setValue(self: Any?, prop: KProperty<*>, value: T)
= field.set(value)
}
You can then use the delegate like this:
val email : String by ObservableDelegate()
fun login(view: View) {
val emailVal = email
}
Read more about delegation of properties here: https://kotlinlang.org/docs/reference/delegated-properties.html
I think it is good enough to use email.get(). If you really want to eliminate the use of .get() in your code, you may use backing field:
val _email = ObservableField<String>()
var email: String
get() = _email.get()
set(value) = _email.set(value)
//use
fun login(view: View) {
val emailVal = email
}

Categories

Resources