I'm trying to access the name of a variable inside an iterator
listOf(someClassVariable, anotherClassVariable, yetAnotherClassVariable).forEach {
if (it.foo()) {
map.add(it, ::it.name)
}
}
but getting unsupported [references to variables aren't supported yet] error at ::it.name. Any ideas/workarounds?
You could do it vice-versa, i.e. having a list of references to your class variables and iterate over them and then get the actual value by calling invoke on it:
listOf(::someClassVariable, ::anotherClassVariable, ::yetAnotherClassVariable).forEach { varRef ->
val varValue = varRef() // assignment optional... you can also just do it the way you want ;-)
if (varValue.foo())
map.add(varValue, varRef.name)
}
Related
I need to delete elements from the database.
That's my code and I don't know if I am right
realm.executeTransaction(
realm1 -> {
RealmResults<UserWordRealm> result = realm1.where(UserWordRealm.class).equalTo("id",id).findAll();
result.deleteAllFromRealm();
}
);
}
In the RealmObject class, the id(PrimaryKey) field must uniquely identify the object. Therefore, there cannot be more than one element with the same id. Using findFirst() instead of findAll() may solve your problem.
To delete an object from a realm, use either the dynamic or static versions of the deleteFromRealm() method of a RealmObject subclass.
realm.executeTransaction(r -> {
UserWordRealm userWordObject = r.where(UserWordRealm.class).equalTo("id", id).findFirst();
userWordObject.deleteFromRealm();
// discard the reference
userWordObject = null;
});
When you have an object already initialized, what is the best kotlin scope function to use for setting multiple properties to this object (I don't need to return the instance of the object but only setting properties)
myObject.run {
text = "",
id = 0,
color = "#111111"
}
or
with(myObject) {
text = "",
id = 0,
color = "#111111"
}
or
myObject.apply {
text = "",
id = 0,
color = "#111111"
}
or maybe "let" ? I'm little confused because all scope functions can be used to achieve that but which is the must appropriated ?
I don't need to return the instance of the object but only setting properties
All scope functions return something. Some return the context object (apply/also) while others return lambda result (let/with/run). If you don't need the return value you can ignore them.
Here you want to mutate the properties of myObject, so apply would make the most sense. It provides the context object as this inside lambda and returns the object itself after executing the lambda.
Using with or run will return Unit (which is the result of lambda). Since you don't care about the result, you go with any of these but as I said earlier, apply makes the most sense here.
Quoting the docs,
Use apply for code blocks that don't return a value and mainly operate on the members of the receiver object. The common case for apply is the object configuration. Such calls can be read as “ apply the following assignments to the object.”
run is useful when your lambda contains both the object initialization and the computation of the return value.
We recommend with for calling functions on the context object without providing the lambda result. In the code, with can be read as “ with this object, do the following.”
Btw, if myObject is a data class, I would suggest using the copy function for creating a new instance by modifying some properties of myObject.
val newObject = myObject.copy(text = "", id = 0, color = "#111111")
I have a two kotlin object which are very identical data without any change, but getting return false. which has to be return true if two object are identical, only if change then it should be return false.
Doing checking objects are:
private var emp1: Employee? = null
var emp2: Employee? = null
fun dataChanged(): Boolean {
return if (emp1 != null && emp2 != null) {
emp1 != emp2
} else {
false
}
}
I checked the data in log, which is not changing anyhing not even space.
Employee defiend as follows,
data class Employee(
//...
): Parcelable {
//...
}
No equals and hashcode.
here using for changing data change on edittext -> TextInputEditText, TextWatcher. Any suggestion, where, i'm doing wrong.
Thanks in advance.
When you define a data class compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair;
...
Therefore equals method execution depends on parameters of primary constructor.
If you use some other objects in primary constructor make sure they are also data classes or have overriden equals method.
I suggest to put logs before comparison of two objects and check whether they contain equal data.
EDIT:
I have a two kotlin object which are very identical data without any change, but getting return false.
Your function dataChanged() returns false for two identical objects because of condition emp1 != emp2. The name of the function says that it will return true if objects are not identical, false - if they are identical, i.e. data not changed. So the function dataChanged() works as expected.
When setting a value to MediatorLiveData that reacts to a source added in the constructor of a viewModel or activity onCreate observer in the ViewModel , like this for example:
showingMethodLiveData.addSource(stateChangeLiveData) {
when (it) {
ConfigurationState.CURRENT -> showingMethodLiveData.value = commMethod[it]
ConfigurationState.PENDING -> showingMethodLiveData.value = commMethod[it]
}
}
The value isn't set to the observing view, although the set method is called.
I can work around this by either adding the source in onStart (which creates other problems of registering observer more than once), or using postValue instead of setValue.
The debug of setValue method leads me to following code, where there is an interesting comment that tells the story, the method returns without setting the value to the binded view.
in androidx.databinding package of lifecycle dependency:
class ViewDataBinding:
private void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
if (mInLiveDataRegisterObserver) {
// We're in LiveData registration, which always results in a field change
// that we can ignore. The value will be read immediately after anyway, so
// there is no need to be dirty.
return;
}
boolean result = onFieldChange(mLocalFieldId, object, fieldId);
if (result) {
requestRebind();
}
}
The value is not set afterwards either, but only when the mediatorlivedata is invoked again by change in it's source.
Why this situation occurs?
Thank you for the help
PS
I think it may be an android library bug
The use of Mediatorlivedata is to compare two values and then provide a result.
If you want to change the value of a variable, you can simply use MutableLiveData and to assign a new value, write variableName.value = newValue
Should be even easier to achieve like this:
val showingMethodLiveData = Transformations.map(stateChangeLiveData) { commMethod[it] }
Why Android Studio show error when I use No.2 script.
I found no different between 1 and 2.
class Adapter {
var nameList : ArrayList<String>? = null
}
class Program {
private fun send() {
val list: ArrayList<String> = ArrayList()
val adapter = Adapter()
// Case 1
var otherList = adapter.nameList
if (otherList != null) {
list.addAll(otherList) // <--- no error
}
// Case 2
if (adapter.nameList!=null) {
list.addAll(adapter.nameList) // <--- Error here
// Smart cast to 'kotlin.collections.ArrayList<String> /* = java.util.ArrayList<String> */' is impossible, because 'adapter.nameList' is a mutable property that could have been changed by this time
}
}
}
Please explain this case
The IDE should give you a warning, explaining that after the null check, it's possible that adapter.nameList was changed by another thread, and that when you call list.addAll(adapter.nameList), adapter.nameList could actually be null by that point (again, because a different thread could have changed the value. This would be a race condition).
You have a few solutions:
Make nameList a val, which makes its reference final. Since it's final, it's guaranteed another thread can't change it. This probably doesn't fit your use case.
class Adapter {
val nameList : ArrayList<String>? = null
}
Create a local copy of name list before you do the check. Because it's a local copy, the compiler knows that another thread can't access it, and thus it can't be changed. The local copy could be defined with either a var or a val in this case, but I recommend val.
val nameList = adapter.nameList
if (nameList != null) {
list.addAll(nameList)
}
Use one of the utility functions that Kotlin provides for just such a case as this. The let function copies the reference it's called on as a parameter using an inline function. This means that it effectively compiles down to be the same as #2, but it's a bit more terse. I prefer this solution.
adapter.nameList?.let { list.addAll(it) }
Your adapter.nameList is mutable property so please convert it to immutable.
Use this
val nameList : ArrayList<String>? = null
Instead of this
var nameList : ArrayList<String>? = null
Or you can also solve this problem by assert of non null Assert
list.addAll(adapter.nameList!!)
Note :- !! is evaluated at runtime, it's just an operator.
The expression (x!!)
throws a KotlinNullPointerException if x == null,
otherwise, it returns x cast to the corresponding non-nullable type (for example, it returns it as a String when called on a variable with type String?).
adapter.nameList is a mutable property that could have been changed`
The reason for this check and error message is threads. What you have is called a race-condition. In many similar cases it is possible for another thread to change the value of adapter.namelist between the nullity check and the list.addAll call. Clearly this can not happen in your case as the adapter is not leaked from the send function, but I guess the compiler isn't smart enough to know that.
In contrast there is no race condition in case 1 as the namelist is only accessed once.
Also this can not happen if namelist is val rather than var - since the compiler then knows it can not change - so it can not change from non-null to null.