I have two objects Object1 and Object2, which may or may not be null.
I want to test null safe equality between them (true if both null, or both equal)
I have done my research and found Objects.equals(Object1,Object2), which fulfills its purpose.
But the problem is that it's only for API level 19+.
How do I make this compatible with lesser APIs?
There are several options.
You could override the
equals() method in the class you are comparing, and use obj1.equals(obj2)
You could use the hashCode() method of Object to determine if they are equivalent, by using obj1.hashCode() == obj2.hashCode()
Just using plain old == determines weather two variables contain the same instance of a class.
If these aren't null safe, you can easily write your own null checking with obj == null
This should be completely independent of the Android Api.
Have you tried Guava?
Guava has some pretty nice functionality that removes alot of the boilerplate.
Here is a link of their example.
Here is a simple comparison method. It seems odd that you have to roll your own, though.
/**
* Null safe comparison of two objects.
* #return true if the objects are identical.
*/
public static boolean objectEquals(Object o1, Object o2) {
if (o1 == null && o2 == null) return true;
if (o1 == null) return false;
return o1.equals(o2);
}
Related
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.
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) }
I'm working on an app in android studio. The part of the app I'm having issues with is where the user can favourite and remove their favourite item. I do this by adding and removing the item from a list.
The thing is the add functionality works which is:
public void addFavorite(Context context, NewSubject subject) {
List<NewSubject> favorites = getFavorites(context);
if (favorites == null)
favorites = new ArrayList<NewSubject>();
favorites.add(subject);
saveFavorites(context, favorites);
}
I am passing in an object of type "NewSubject" which is just a class of getters and setters for name and id.
The problem arises when I try to remove an item from this list. Code below:
public void removeFavorite(Context context, NewSubject subject) {
ArrayList<NewSubject> favorites = getFavorites(context);
if (favorites != null) {
favorites.remove(subject);
saveFavorites(context, favorites);
}
}
I've even tried something like:
for(int i = 0; i < favorites.size(); i++){
if(favorites.get(i).getSubject_name() == subject.getSubject_name())
favorites.remove(i);
}
Even though both subject names match, the if statement never triggers as true. By changing it to ID it does remove the item but I was wondering why it doesn't work the other way. MeetTitan suggested to use "equals" operator to compare Strings and this has fixed that issue. But I'm still wondering as to why removing the item by "subject" without the FOR loop and IF statement doesn't work.
I have cleared the app's data multiple times whilst trying to debug the source of the problem.
Thank you for your time and help, it is much appreciated.
This applies if you are re-creating NewSubject twice... If you are trying to remove the exact same instance of NewSubject that you got from the collection, then I guessed wrong and this isn't the answer you are looking for.
Is it possible you haven't defined equals and hashCode in your Favorites object? Without those remove will only work with the EXACT same object instance in the collection. If you haven't, try defining them and see if remove() works the way you expect.
Without those methods defined, collections will respond this way:
Obj x=new Obj("data")
Obj y=new Obj("data")
collection.put(x)
collection.remove(y)
assert( collection.size() == 1) // was not removed because .equals didn't exist--remove reverted to == instead which failed, x != y
collection.remove(x)
assert( collection.size() == 0) // NOW it worked because you used the same instance.
if you define .equals and hashCode to compare the strings inside obj, then this will work:
collection.put(x)
collection.remove(y)
assert( collection.size() == 0) // worked because x.equals(y)!
Try
String.equalsIgnoreCase(value1,value2)
This might do your work.
From your example, it's evident that name is a String object. In java, you have to use ".equals()" or comparing two strings.
You can do this:
if(favorites.get(i).getSubject_name().equals(subject.getSubject_name())){
...
}
Or, you can override the equals() method in your NewSubject class to make this work:
favorites.remove(subject);
You can use something like this as your equals() method in the NewSubject class (considering you are only matching two NewSubject objects based on their names):
#Override
public boolean equals(Object other){
if (other == null) return false;
if (other == this) return true;
NewSubject otherSubject = (NewSubject) other;
if(this.getSubject_name().equals(otherSubject.getSubject_name()))
return true;
else
return false;
}
Update:
You may want to override hashcode() as well. If your NewSubject class ever gets used in a hash-based collection such as HashMap, overriding only equals() method will not be sufficient. For reference, this is from Effective Java by Joshua Bloch:
You must override hashCode() in every class that overrides equals().
Failure to do so will result in a violation of the general contract
for Object.hashCode(), which will prevent your class from functioning
properly in conjunction with all hash-based collections, including
HashMap, HashSet, and Hashtable.
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.
In the Android dev guide there is an example on how to implement the speech recognition. That example prints a list of results using an array.
What if I'm only interested in the first result?
I have implemented it this way (data is the Intent returned by the Activity result):
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).get(0)
Is there another way to get just the first result without getting an array? Something like a getFirst method?
No, the api does not specify some kind of getFirst method.
If you use this in different places, you can create one for yourself, which does a null check and returns the first result:
public String getFirst(Intent data){
List<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
if(results != null && results.size() > 0){
return results.get(0);
}
return null; //or maybe: return "";
}
The object returned from getStringArrayListExtra() is a ArrayList, which does not offer a getFirst() call. You could call iterator() and get the first entry on that via next(), but get(0) will probably be more efficient, because it is a plain array access in contrast to an object creation.