Comparing two object are indentical in kotlin - android

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.

Related

transformations.switchMap works only once or possibly not at all

I am looking for some help with transformations.switchMap. I somehow can't get it to work and start to wonder if I actually understand things correctly.
Based on my understanding the first parameter is like a trigger, whenever it's values changes the, second parameter/function will be mapped/called to return a new/modified liveData object.
In my case I have:
#MainActivity
viewModel.repository.items.observe(requireActivity(), Observer { weight -> weight?.let { adapter!!.setItems(it) } })
#ViewModel
lateinit var items: LiveData<List<Weight>> # Used in my RecyclerView
var filterChanged = MutableLiveData<Long>(0L) # The "trigger" value
override fun init() {
super.init()
items = Transformations.switchMap(filterChanged) {repository.getItems(0L, filterEnd)}
}
repository.getItems() returns a live data object filtered by filterEnd (SQL ...where timestamp <= filterEnd), with timestamp being a date in numerical representation.
This works! But only once, during initialisation of the Fragment. Once the fragment has initialised I can change the filterChanged trigger variable as often as I want,
the repository.getItems() function does not get called a second time (applying an updated value for filterEnd).
It requires start/stop of a secondary activity (settings or about) which will recreate my viewModel to update the filter value.
I saw several ways of crafting the Transformations.switchMap statement, using lambdas, one parameter or two, etc., but I either couldn't get them to compile or they didn't do what I expected.
Now, have I understood correctly how this is supposed to work? Any idea what I might be doing wrong?
Edit: Added the repository section to clarify things:
#Repository
items = getItems(filterStart, filterEnd) # This is where I enter from the ViewModel
// This function redirects to the actual dao functions,
fun getItems(filterStart: Long, filterEnd: Long): LiveData<List<Weight>> {
Log.d("Repository ", "getItems: $filterStart, $filterEnd")
if ((filterStart != 0L) && (filterEnd == 0L)) return dao.getItemsGreaterThan(filterStart)
else if ((filterStart == 0L) && (filterEnd != 0L)) return dao.getItemsLessThan(filterEnd)
else if ((filterStart != 0L) && (filterEnd != 0L)) return dao.getItemsRange(filterStart, filterEnd)
else return dao.getItems()
}
At one point I simplified things and called the dao functions directly, like so:
items = dao.getItemsLessThan(filterEnd)
Even bypassed the repository allt ogether and called the respective dao function directly from the ViewModel, but that didn't make any difference either.
And then there are the corresponding dao functions, like the below:
#Query("SELECT * from weight WHERE timestamp >=:start ORDER BY timestamp DESC")
abstract fun getItemsGreaterThan(start: Long): LiveData<List<Weight>>

Android MVVM: databinding value is not set from MediatorLiveData in particular situation

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] }

Which one should I use between if (data!=null) and data?.let in kotlin?

The Code A is common usage in java.
I havn't understanded completely the key let of Kotin. Which one should I use between Code A and Code B in kotlin? Thanks!
Code A
if (data!=null){
initAndBindAdapter()
mCustomAdapter.setSelectedIndex(data.getIntExtra("index",-1))
}
Code B
data?.let {
initAndBindAdapter()
mCustomAdapter.setSelectedIndex(it.getIntExtra("index",-1))
}
And more, which one should I choose between Code C and Code D in kotlin if the fun do_NoNeed_data_par doesn't need data parameter ?
Code C
if (data!=null){
do_NoNeed_data_par()
}
Code D
data?.let {
do_NoNeed_data_par()
}
I (personal opinion) think it's a good idea to use simple, regular null checks where you can, although the ?.let method has been listed under the main Kotlin Idioms page of the documentation (which is open for the community to contribute) - so basically, this will be up to your personal preferences of which one is more readable.
The more interesting question is what are the differences, and when you can use each: the main difference is that let holds on to the value of the variable as it was when the let call on it started, and any subsequent uses of it within the let block will reference that same value. If you use a simple null check with if, your variable's value might be changed while the body of the if block is being executed.
So for example, this won't compile, because x can be accessed by multiple threads, and it might be non-null when you read its value first for the null check, but it might become null by the time you read it again for the println parameter - this would be unsafe:
class Foo {
var x: Int? = null
fun useX() {
if (x != null) {
println(x + 10) // (...) 'x' is a mutable property that could have been changed by this time
}
}
}
However, a let will work in the same situation, because it will use whatever the initial value of x had all throughout its execution, even if the x property in the class gets reassigned in the meantime:
class Foo {
var x: Int? = null
fun useX() {
x?.let {
println(it + 10)
}
}
}
You can think of the ?.let statement above of basically performing this, creating a temporary copy of your variable:
fun useX() {
val _x = x
if (_x != null) {
println(_x + 10)
}
}
Operating on this copy is safe, because even if the x property changes its value, this _x copy will either stay null for this entire function, or it's non-null and safe to use.
"should" is opinionated. It all depends on your preference.
If you prefer more functional style code then .let is your answer. If you prefer more procedural code then == null is your answer.
Sometimes, using let() can be a concise alternative for if. But you have to use it with sound judgment in order to avoid unreadable “train wrecks”. Nevertheless, I really want you to consider using let().
val order: Order? = findOrder()
if (order != null){
dun(order.customer)
}
With let(), there is no need for an extra variable. So we get along with one expression.
findOrder()?.let { dun(it.customer) }

Kotlin "Smart cast is impossible, because the property could have been changed by this time"

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.

if ArrayList contains doesn't work

i'm curently developing app which has lots of ArraYlists and it needs to compare them with nonlist data. When i try this method fdata.contains(data2) it always returns false. ArayLists contains class named 'favdat' which looks like this:`
public class favdat {
public String product,term,note,link;
}
And Data2 is defined like this: favdat Data2=new favdat();
I have also tryed retain all method and it returns list in the size of 0.
I know that some data are equal.
So the question is how could i tell if fdata contains data2?
The default implementation to compare objects is to compare if they are the same object, so two objects with the exactly same attributes would still not be equals. What you need to do is override the hashCode and equals methods. As an example:
public int hashCode() {
return product.hashCode() * 31 + term.hashCode();
}
public boolean equals(Object o) {
if (o instanceof favdata) {
favdata other = (favdata) o;
return product.equals(other.product)
&& term.equals(other.term)
&& note.equals(other.note)
&& link.equals(other.link);
} else {
return false;
}
}
In java classnames are usually started with capitals, so it would be Favdat, and your code usually gets easier to read to keep the field declarations separate.
You need to define a method called equals(Object obj) inside favdat that will enable object comparison.
Here's a more detailed howto:
Comparing Objects in Java
Also related thread in SO, talk about how ArrayList.contains() work.

Categories

Resources