Why arguments in Kotlin Triple class define as val?
public data class Triple<out A, out B, out C>(
public val first: A,
public val second: B,
public val third: C
) : Serializable {
/**
* Returns string representation of the [Triple] including its [first], [second] and [third] values.
*/
public override fun toString(): String = "($first, $second, $third)"
}
Is there a way for changing first , second or third value after we set them ?
private var mSituation=Triple<Boolean,Boolean,Boolean>(first = false, second = false, third = false)
mSituation.first=true // val cannot be reassigned
You can use copy method of data classes
private var mSituation = Triple(first = false, second = false, third = false)
mSituation = mSituation.copy(first = true)
But you shouldn't use Triple this way, no one will understand what these values mean, even you will forget in a few weeks
Create your own data class with meaningful property names and var modifier, if you need it
Properties in Kotlin classes can be declared either as mutable using the var keyword, or as read-only using the val keyword.
public class Triple< A>(
public var first: A
)
fun main() {
var mSituation=Triple<Boolean>(first = false)
mSituation.first=true
println("Hello, world!!!")
}
https://kotlinlang.org/docs/reference/properties.html
Related
Hello everyone I am new to Jetpack Compose.
Needed clarification reagarding the usage of Copy Function in kotlin Data Classes.
Here i am using a NetworkFetchState abstract class, which helps me determine the state of the network call.
// Abstract Class
abstract class NetworkFetchState(
val isLoading: Boolean = false,
val isSuccess: Boolean = false,
val isError: Boolean = false,
val error: Throwable? = null,
val errorMessage: String? = null
)
I am creating the data class that is extending this abstract class
data class LoginDataState(
val responseData: LoginResponse? = null
) : NetworkFetchState() // extending the Abstract Class
Now inside the ViewModel Class i am creating a mutable state flow
class MyViewModel:ViewModel(){
// Mutable State Flow of the Data State
private val _loginDataState = MutableStateFlow(LoginDataState())
// readonly value of the __loginDataState
val loginDataState: StateFlow<LoginDataState> get() = _loginDataState
/*
* Here I am performing network calls inside the view model scope
* based on the result from the network call i am trying to update the MutableStateFlow
*/
fun makeNetworkCall(){
// ....
_loginDataState.update { prevState ->
prevState.copy(
// ---- PROBLEM HERE ----
// isLoading, isSuccess.. etc (all other variables from abstract class)
// are not available
)
}
}
}
all the member variables that are extending from abstract class are not visible.
What am i doing wrong?
The .copy function is a function generated by kotlin compiler for all data classes. As per the documentation, it's using only properties declared in the primary constructor.
If you want to change those properties with copy function, you will have to add them to the primary constructor somehow.
// this would work
data class LoginDataState(
val responseData: LoginResponse? = null,
val _isLoading: Boolean = false,
) : NetworkFetchState(isLoading = _isLoading)
// this is probably better
interface NetworkFetchState {
val isLoading: Boolean get() = false
}
data class LoginDataState(
val responseData: LoginResponse? = null,
override val isLoading: Boolean = false,
) : NetworkFetchState
I downloaded a project and I'm not really sure what exactly the following line does:
val (episode, setEpisode) = remember { mutableStateOf<EpisodeDetail?>(null) }
The only thing I don't get is why there are two names after the "val" word.
I tried to google for it but I really don't know the name of the syntax.
It's called a Destructuring Declaration
https://kotlinlang.org/docs/destructuring-declarations.html
You may have seen something similar in JavaScript when you have an object, and you can extract the keys to variables with the following
const { key1, key2 } = { key1:"first", key2:"second", ignored:"third" };
console.log(key1, key2) // first second
If you have a data class Kotlin will create the component<N> functions for you.
class MyClass (val myStr: String, val myInt: Int, val myBool: Boolean) {
operator fun component1(): String = myStr
operator fun component2(): Int = myInt
operator fun component3(): Boolean = myBool
}
fun main() {
val x = MyClass("Hello", 5, false)
val (y, _, z) = x // use _ to ignore values you don't need
println(y) // Hello
println(z) // false
}
Unlike Javascript which uses the key names, Kotlin data classes use field ordering (by defining your own component<N> functions you could swap the order of destructuring).
I have a LiveData property for login form state like this
private val _authFormState = MutableLiveData<AuthFormState>(AuthFormState())
val authFormState: LiveData<AuthFormState>
get() =_authFormState
The AuthFormState data class has child data objects for each field
data class AuthFormState (
var email: FieldState = FieldState(),
var password: FieldState = FieldState()
)
and the FieldState class looks like so
data class FieldState(
var error: Int? = null,
var isValid: Boolean = false
)
When user types in some value into a field the respective FieldState object gets updated and assigned to the parent AuthFormState object
fun validateEmail(text: String) {
_authFormState.value!!.email = //validation result
}
The problem is that the authFormState observer is not notified in this case.
Is it possible to trigger the notification programically?
Maybe you can do:
fun validateEmail(text: String) {
val newO = _authFormState.value!!
newO.email = //validation result
_authFormState.setValue(newO)
}
You have to set the value to itself, like this: _authFormState.value = _authFormState.value to trigger the refresh. You could write an extension method to make this cleaner:
fun <T> MutableLiveData<T>.notifyValueModified() {
value = value
}
For such a simple data class, I would recommend immutability to avoid issues like this altogether (replaces all those vars with vals). Replace validateEmail() with something like this:
fun validateEmail(email: String) = //some modified version of email
When validating fields, you can construct a new data object and set it to the live data.
fun validateFields() = _authFormState.value?.let {
_authFormState.value = AuthFormState(
validateEmail(it.email),
validatePassword(it.password)
)
}
In android there is a class called MediatorLiveData<T> which acts as a ... mediator for LiveData.
There are situations like when we need to validate multiple live data objects and get a boolean value from them; we make a CombinedLiveData<T,U,S> and extend MediatorLiveData<S> so we can pass a higer-order function which accepts t:T, u:U and returns an S.
Imagine when we have to pass unlimited amount of Ts to this class and return a value of S: We have to make a CLD_12<T1,T2,...T12,S> and another one for 13 inputs, very frustrating.
So I am asking if there is a way to say CLD<T1..., S> : MLD<S>() or another way make this happen.
edit: adding the code
class CombinedLiveData2<T, K, S>(
source1: LiveData<T>,
source2: LiveData<K>,
private val combine: (data1: T?, data2: K?) -> S
) : MediatorLiveData<S>() {
private var data1: T? = null
private var data2: K? = null
init {
super.addSource(source1) {
data1 = it
value = combine(data1, data2)
}
super.addSource(source2) {
data2 = it
value = combine(data1, data2)
}
}
}
Right code:
class MainActHandler(val weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code:
class MainActHandler(weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code screenshot
The only difference is the "val" has been deleted and cannot be resolve.
Which might be important is that it's a inner class.
BUT
This one class without "val/var" in constructor parameter is working:
class BookInfo(convrMgr: ConversationMgr, id: String, queue: RequestQueue, queueTag:String) {
val TAG = "BookInfo"
var title: String? = ""
init {
val url = "https://api.douban.com/v2/book/$id"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
Log.d(TAG + " Response", response.substring(0))
// Parse JSON from String value
val parser = Parser()
val jsonObj: JsonObject =
parser.parse(StringBuilder(response.substring(0))) as JsonObject
// Initial book title of book properties.
title = jsonObj.string("title")
Log.d(TAG + " Book title", title)
convrMgr.addNewMsg(title)
},
Response.ErrorListener { error -> Log.e(TAG + " Error", error.toString()) })
// Set the tag on the request.
stringRequest.tag = queueTag
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
}
And if I add var/val before "queue: RequestQueue", I'll get suggestion:
"Constructor parameter is never used as a property less. This inspection reports primary constructor parameters that can have 'val' or 'var' removed. Unnecessary usage of 'val' and 'var' in primary constructor consumes unnecessary memory."
I am just confused about it.
When you write val/var within the constructor, it declares a property inside the class. When you do not write it, it is simply a parameter passed to the primary constructor, where you can access the parameters within the init block or use it to initialize other properties. For example,
class User(val id: Long, email: String) {
val hasEmail = email.isNotBlank() //email can be accessed here
init {
//email can be accessed here
}
fun getEmail(){
//email can't be accessed here
}
}
Constructor parameter is never used as a property
This suggestion is saying that you do not use this property in place apart from the initialization. So, it suggests you to remove this property from the class.
Constructor parameters must use var or val when they are used as a property elsewhere in the class. They do not need to be properties if they are only used for class initialization.
In the example below, the parameter must be a property (var or val) because it is used in a method:
class A(val number: Int) {
fun foo() = number
}
In this other example, the parameter is only used to initialize the class, so it does not need to be a property:
class B(number: Int): A(number) {
init {
System.out.println("number: $number")
}
}
This might be a late answer but the magic lies under the hood:
Based on #BakaWaii's answer:
Putting var/val will make the variable a property of the class and not putting it will make it a parameter of only the constructor function.
So what does it mean, to understand lets look into some code:
class Test(a: Int){}
Now Lets see the decompiled java code:
public final class Test {
public Test(int a) {
}
}
So now if I try to access a using the object of Test() like the below code:
Test t = new Test(10);
t.a //Error
It will give me error. Unresolved reference: a. Why because a is a parameter of the constructor only.
Now if we put var/val in the paramater like below:
class Test(var a: Int){}
The decompliked Java code will become:
public final class Test {
private int a;
public final int getA() {
return this.a;
}
public final void setA(int var1) {
this.a = var1;
}
public Test(int a) {
this.a = a;
}
}
Thus it will not only give you a class property but also give you getter/setters for setting the values.
Now the next question arises if the field a is private how can it be accessed. Simple answer in Java you cannot, i.e. if you are calling the KT class from a Java you will not be able to assign value of a like Test(1).a = 10 but will have to use Test(1).setA(5).
But as kotlin internally handles getters/setters Test(1).a = 5 will be ok.
For #Parcelize to work you need to open up the super's properties and override them in the child:
abstract class Goal(open var number: Int, open var name: String) : Parcelable
#Parcelize
class OperationalGoal(override var number: Int, override var name: String, var description: String) : Goal(number, name)```
In very simple terms, use var or val in class constructor parameters when you want to use that variable, say, inside a method within that class. Thus you're effectively turning them into properties and not just mere constructor or method parameters.
class User(var name: String, age: Int) {
var str = "John"
var num = 18
fun setName(){
name = str // due to using var on our class constructor parameter, we can access the constructor variable *name* inside this setter method. *name* is a property parameter thanks to the var keyword.
}
fun setAge(){
age = num // this will result in a compiler error, because *age* is just a parameter, notice that var wasn't used in the *age* parameter within the class constructor, which means we can't access it like we did with *name*
}
}
Run this Kotlin Playground code to get a clearer idea of what's going on.