I have an assignment to implement a generic Pair interface that represents ordered pairs like (x,y). I'm trying to write the equals method, but I have to make the parameter a general object instead of a pair, so I don't know how to get the coordinates of it. I get a symbol not found error when I try to compile because the Object class doesn't have a fst and snd method. Should I be throwing an exception or something? Note: My professor gave us a template and I'm just filling in the methods, I don't think I'm allowed to change params or methods.
Here's relevant code:
public class Pair<T1,T2> implements PairInterface<T1,T2>
{
private T1 x;
private T2 y;
public Pair(T1 aFirst, T2 aSecond)
{
x = aFirst;
y = aSecond;
}
/**
* Gets the first element of this pair.
* #return the first element of this pair.
*/
public T1 fst()
{
return x;
}
/**
* Gets the second element of this pair.
* #return the second element of this pair.
*/
public T2 snd()
{
return y;
}
...
/**
* Checks whether two pairs are equal. Note that the pair
* (a,b) is equal to the pair (x,y) if and only if a is
* equal to x and b is equal to y.
* #return true if this pair is equal to aPair. Otherwise
* return false.
*/
public boolean equals(Object otherObject)
{
if(otherObject == null)
{
return false;
}
if(getClass() != otherObject.getClass())
{
return false;
}
T1 a = otherObject.fst();
T2 b = otherObject.snd();
if (x.equals(a) && y.equals(b))
{
return true;
}
else
{
return false;
}
}
These are the errors I get:
./Pair.java:66: cannot find symbol
symbol : method fst()
location: class java.lang.Object
T1 a = otherObject.fst();
^
./Pair.java:67: cannot find symbol
symbol : method snd()
location: class java.lang.Object
T2 b = otherObject.snd();
^
The parameter to the equals method is an Object, which is not guaranteed to have your methods fst or snd, hence the compiler error. To be able to call those methods, you need to have a Pair object.
It is standard practice to test the class of the object passed in, to see if it's even the same class as this, returning false if it's not the same class. Usually that is done with instanceof, followed by a cast if true. The cast allows the compiler to treat the object as if it is the class that you say it is. This allows the compiler to find the methods you want to call.
if(otherObject == null)
{
return false;
}
// Use instanceof
if(!(otherObject instanceof Pair))
{
return false;
}
// Cast - use <?, ?>; we don't know what they are.
Pair<?, ?> otherPair = (Pair<?, ?>) otherObject;
Object a = otherPair.fst();
Object b = otherPair.snd();
// Simplified return
return (x.equals(a) && y.equals(b));
after class comparison if(getClass() != otherObject.getClass()) return false;
you need to create new variable with proper type, like this:
Pair<T1,T2> pair = (Pair<T1,T2>)otherObject;
then you will be able to use pair and its methods
After you have ensured that getClass() != otherObject.getClass() is true, you may perform a safe cast to Pair<?, ?> pair = (Pair<?, ?>) otherObject;. Then, use your code as it is. Note: You cannot perform a safe cast to Pair<T1, T2>, as you only know that otherObject is an instance of Pair<?, ?>. Your comparison with .equals() should still work, however.
You have to cast the other object explicitly to be a pair, first (considering you already know it's a Pair object because the classes are the same), but you don't know what type it has, so you should use a wild card.
Pair<?, ?> otherPair = (Pair<?, ?>)otherObject;
//add null-checks if fst() or snd() is nullable
if(this.fst().equals(otherPair.fst()) && this.snd().equals(otherPair.snd()) {
return true;
} else {
return false;
}
You can pass any object type to equals(), so part of properly implementing equals is to check the type of the argument Object.
If you look at the java core classes, they usually start by checking == and then whether it's an instanceof the class implementing equals().
Generally speaking, you should check:
1) == equality, ie, whether the argument is a reference to the current object itself- if it is, the objects should be equal per equals(). This is computationally cheap.
2) whether the argument is null. This is also computationally cheap and if true, ensures that equals() returns false.
3) whether or not the argument object is an instance of the current object's class. If it isn't it can't be equal and the cast will fail anyway.
4) then cast it and start comparing the parts to one another
Lazy programmer tip- eclipse can automatically generate nice java equals() blocks using a wizard. It's in right click > source > generate hashcode() equals() and bam, it generates a proper equals() method.
Cast your otherObject to a Pair before calling its methods.
Pair<T1, T2> otherPair = (Pair) otherObject;
T1 a = otherPair.fst();
T2 b = otherPair.snd();
It's because you do not cast otherObject first, so JVM think it was of type Object. An Object does not have methods fst() and snd(), so it fails.
To solve, cast otherObject to Pair then call fst() and snd():
public boolean equals(Object otherObject)
{
if(otherObject == null)
{
return false;
}
if(getClass() != otherObject.getClass())
{
return false;
}
T1 a = ((Pair<T1, T2>)otherObject).fst();
T2 b = ((Pair<T1, T2>)otherObject).snd();
if (x.equals(a) && y.equals(b))
{
return true;
}
else
{
return false;
}
}
Related
This is what I wrote.
public class Test {
public static void main(String[] args) {
Object a1 = new A();
Object a2 = new Object();
System.out.println(a1.toString());
System.out.println((a1 == a2) + " " + (a1.equals(a2)));
}
}
class A {
int x;
public boolean equals(Object obj) {
A _obj = (A) obj;
return x == _obj.x;
}
public String toString() {
return "A's x is " + x;
}
}
How can I make 'false true' on the console? Except revising the main method. Revise only A method.
I tried to make change the Object a2 to an a2. How Can I change that in the A class?
The reason you're getting the error class java.lang.Object cannot be cast to class A is because the object you're comparing it to is not an instance of class A, so trying to cast the object as such will fail.
When implementing the .equals method, you should always perform these three checks first to ensure the safety of the object before you try comparing its properties:
if (obj == this) return true; If the two objects are the exact same object, meaning that they are the same instance, not just two objects with the same properties, immediately return true because there is no need to check the properties.
if (obj == null) return false; This prevents a NullPointerException by trying to access a property of a null object (such as when in your code you do return x == _obj.x)
if (!(obj instanceof A)) return false; If the object is not an instance of your class, the typecast will fail (as it did in your code) and this protects against that by returning false before trying to cast.
Finally, if the code reaches this point you can cast and compare the objects as you had done in your code:
A _obj = (A) obj;
return this.x == _obj.x;
Keep in mind that if the properties you are comparing are not primitives, you should use .equals on them
First of all, what do you mean by "making false true" exactly? I assume you want your code to run, but could you give us a bit more context of what you are trying to do?
The reason your code fails is that you are trying to cast your instance of an Object (a2) onto a reference of type A when you pass it into the equals method. But since a2 is actually a pure instance of Object and not of A, this cast fails. Even though Object is the baseclass for everything in Java, including your self defined A, you are casting in the wrong direction. An Object does not hold an attribute x so casting this way would be unsafe. Java's typechecking mechanism catches this and throws an error when you try to cast.
Have a look at a document explaining inheritance and casting to get the basics of this. E.g., this one.
Say I create one object and add it to my ArrayList. If I then create another object with exactly the same constructor input, will the contains() method evaluate the two objects to be the same? Assume the constructor doesn't do anything funny with the input, and the variables stored in both objects are identical.
ArrayList<Thing> basket = new ArrayList<Thing>();
Thing thing = new Thing(100);
basket.add(thing);
Thing another = new Thing(100);
basket.contains(another); // true or false?
class Thing {
public int value;
public Thing (int x) {
value = x;
}
equals (Thing x) {
if (x.value == value) return true;
return false;
}
}
Is this how the class should be implemented to have contains() return true?
ArrayList implements the List Interface.
If you look at the Javadoc for List at the contains method you will see that it uses the equals() method to evaluate if two objects are the same.
I think that right implementations should be
public class Thing
{
public int value;
public Thing (int x)
{
this.value = x;
}
#Override
public boolean equals(Object object)
{
boolean sameSame = false;
if (object != null && object instanceof Thing)
{
sameSame = this.value == ((Thing) object).value;
}
return sameSame;
}
}
The ArrayList uses the equals method implemented in the class (your case Thing class) to do the equals comparison.
Generally you should also override hashCode() each time you override equals(), even if just for the performance boost. HashCode() decides which 'bucket' your object gets sorted into when doing a comparison, so any two objects which equal() evaluates to true should return the same hashCode value(). I cannot remember the default behavior of hashCode() (if it returns 0 then your code should work but slowly, but if it returns the address then your code will fail). I do remember a bunch of times when my code failed because I forgot to override hashCode() though. :)
It uses the equals method on the objects. So unless Thing overrides equals and uses the variables stored in the objects for comparison, it will not return true on the contains() method.
class Thing {
public int value;
public Thing (int x) {
value = x;
}
equals (Thing x) {
if (x.value == value) return true;
return false;
}
}
You must write:
class Thing {
public int value;
public Thing (int x) {
value = x;
}
public boolean equals (Object o) {
Thing x = (Thing) o;
if (x.value == value) return true;
return false;
}
}
Now it works ;)
Just wanted to note that the following implementation is wrong when value is not a primitive type:
public class Thing
{
public Object value;
public Thing (Object x)
{
this.value = x;
}
#Override
public boolean equals(Object object)
{
boolean sameSame = false;
if (object != null && object instanceof Thing)
{
sameSame = this.value == ((Thing) object).value;
}
return sameSame;
}
}
In that case I propose the following:
public class Thing {
public Object value;
public Thing (Object x) {
value = x;
}
#Override
public boolean equals(Object object) {
if (object != null && object instanceof Thing) {
Thing thing = (Thing) object;
if (value == null) {
return (thing.value == null);
}
else {
return value.equals(thing.value);
}
}
return false;
}
}
Other posters have addressed the question about how contains() works.
An equally important aspect of your question is how to properly implement equals(). And the answer to this is really dependent on what constitutes object equality for this particular class. In the example you provided, if you have two different objects that both have x=5, are they equal? It really depends on what you are trying to do.
If you are only interested in object equality, then the default implementation of .equals() (the one provided by Object) uses identity only (i.e. this == other). If that's what you want, then just don't implement equals() on your class (let it inherit from Object). The code you wrote, while kind of correct if you are going for identity, would never appear in a real class b/c it provides no benefit over using the default Object.equals() implementation.
If you are just getting started with this stuff, I strongly recommend the Effective Java book by Joshua Bloch. It's a great read, and covers this sort of thing (plus how to correctly implement equals() when you are trying to do more than identity based comparisons)
Shortcut from JavaDoc:
boolean contains(Object o)
Returns true if this list contains the specified element. More formally,
returns true if and only if this list contains at least one element e such
that (o==null ? e==null : o.equals(e))
record overrides equals
You said:
another object with exactly the same constructor input
… and …
Assume the constructor doesn't do anything funny with the input, and the variables stored in both objects are identical.
As other Answers explain, you must override the Object#equals method for List#contains to work.
In Java 16+, the record feature automatically overrides that method for you.
A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably. By default, you simply declare the member fields. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
The logic of equals by default is to compare each and every member field of one object to the counterpart in another object of the same class. Likewise, the default implementations of hashCode and toString methods also consider each and every member field.
record Thing( int amount ) {} ;
That’s it, that is all the code you need for a fully-functioning read-only class with none of the usual boilerplate code.
Example usage.
Thing x = new Thing( 100 ) ;
Thing y = new Thing( 100 ) ;
boolean parity = x.equals( y ) ;
When run.
parity = true
Back to your List#contains question.
Thing x = new Thing( 100 );
List < Thing > things =
List.of(
new Thing( 100 ) ,
new Thing( 200 ) ,
new Thing( 300 )
);
boolean foundX = things.contains( x );
When run.
foundX = true
Bonus feature: A record can be declared locally, within a method. Or like a conventional class you can declare a record as a nested class, or as a separate class.
I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal. Now I store some instances of that class in a HashSet so that there are no instances with the same names in the HashSet.
My Question: How is it possible to check if the HashSet contains such an object. .contains() wont work in that case, because it works with the .equals() method. I want to check if it is really the same object.
edit:
package testprogram;
import java.util.HashSet;
import java.util.Set;
public class Example {
private static final Set<Example> set = new HashSet<Example>();
private final String name;
private int example;
public Example(String name, int example) {
this.name = name;
this.example = example;
set.add(this);
}
public boolean isThisInList() {
return set.contains(this);
//will return true if this is just equal to any instance in the list
//but it should not
//it should return true if the object is really in the list
}
public boolean remove() {
return set.remove(this);
}
//Override equals and hashCode
}
Sorry, my english skills are not very well. Please feel free to ask again if you don't understand what I mean.
In your situation, the only way to tell if a particular instance of an object is contained in the HashSet, is to iterate the contents of the HashSet, and compare the object identities ( using the == operator instead of the equals() method).
Something like:
boolean isObjectInSet(Object object, Set<? extends Object> set) {
boolean result = false;
for(Object o : set) {
if(o == object) {
result = true;
break;
}
}
return result;
}
The way to check if objects are the same object is by comparing them with == to see that the object references are equal.
Kind Greetings,
Frank
You will have to override the hashCode method also.
try this..
Considering only one property 'name' of your Objects to maintain uniqueness.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
User other = (User) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal.
This breaks the contract of .equals, and you must never do it no matter how convenient it seems.
Instead, if you want to index and look up elements by a certain attribute such as the name, use a HashMap<Name, YourType> to find them. Alternatively, use a TreeSet and pass it a Comparator that compares the name only. You can then remove the incorrect equals method.
There are then three ways if you want to find objects by reference equality:
Your objects have no inherent or useful notion of equality.
Don't implement equals. Leave it to its default. You can then use a HashSet to look for reference equality, and a HashMap or TreeSet to index them by any specific attributes.
Your objects do have a useful, universal notion of equality, but you want to find equivalent instances efficiently anyways.
This is almost never the case. However, you can use e.g. an Apache IdentityMap.
You don't care about efficiency.
Use a for loop and == every element.
HashSet contains uses the equals method to determine if the object is contained - and duplicates are not kept within the HashSet.
Assuming your equals and hashcode are only using a name field...
HashSet<MyObject> objectSet = new HashSet<MyObject>();
MyObject name1Object = new MyObject("name1");
objectSet.add(new MyObject("name1"));
objectSet.add(name1Object);
objectSet.add(new MyObject("name2"));
//HashSet now contains 2 objects, name1Object and the new name2 object
//HashSets do not hold duplicate objects (name1Object and the new object with name1 would be considered duplicates)
objectSet.contains(new MyObject("name1")) // returns true
objectSet.contains(name1Object) // returns true
objectSet.contains(new MyObject("name2")) // returns true
objectSet.contains(new MyObject("name3")) // returns false
If you wanted to check if the object in the HashSet is the exact object you are comparing you would have to pull it out and compare it directly using ==
for (MyObject o : objectSet)
{
if (o == name1Object)
{
return true;
}
}
If you do this alot for specific objects it might be easier to use a HashMap so you don't have to iterate through the list to grab a specific named Object. May be worth looking into for you because then you could do something like this:
(objectMap.get("name") == myNameObject) // with a HashMap<String, MyNameObject> where "name" is the key string.
I am trying to override equals method in Java. I have a class People which basically has 2 data fields name and age. Now I want to override equals method so that I can check between 2 People objects.
My code is as follows
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
But when I write age.equals(other.age) it gives me error as equals method can only compare String and age is Integer.
Solution
I used == operator as suggested and my problem is solved.
//Written by K#stackoverflow
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K#stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output:
run:
-- Subash Adhikari - VS - K false
-- Subash Adhikari - VS - StackOverflow false
-- Subash Adhikari - VS - Subash Adhikari true
-- K - VS - StackOverflow false
-- K - VS - Subash Adhikari false
-- StackOverflow - VS - Subash Adhikari false
-- BUILD SUCCESSFUL (total time: 0 seconds)
Introducing a new method signature that changes the parameter types is called overloading:
public boolean equals(People other){
Here People is different than Object.
When a method signature remains the identical to that of its superclass, it is called overriding and the #Override annotation helps distinguish the two at compile-time:
#Override
public boolean equals(Object other){
Without seeing the actual declaration of age, it is difficult to say why the error appears.
I'm not sure of the details as you haven't posted the whole code, but:
remember to override hashCode() as well
the equals method should have Object, not People as its argument type. At the moment you are overloading, not overriding, the equals method, which probably isn't what you want, especially given that you check its type later.
you can use instanceof to check it is a People object e.g. if (!(other instanceof People)) { result = false;}
equals is used for all objects, but not primitives. I think you mean age is an int (primitive), in which case just use ==. Note that an Integer (with a capital 'I') is an Object which should be compared with equals.
See What issues should be considered when overriding equals and hashCode in Java? for more details.
Item 10: Obey the general contract when overriding equals
According to Effective Java, Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply:
Each instance of the class is inherently unique. This is true for classes such as Thread that represent active entities rather than values. The equals implementation provided by Object has exactly the right behavior for these classes.
There is no need for the class to provide a “logical equality” test. For example, java.util.regex.Pattern could have overridden equals to check whether two Pattern instances represented exactly the same regular expression, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is ideal.
A superclass has already overridden equals, and the superclass behavior is appropriate for this class. For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap.
The class is private or package-private, and you are certain that its equals method will never be invoked. If you are extremely risk-averse, you can override the equals method to ensure that it isn’t invoked accidentally:
The equals method implements an equivalence relation. It has these properties:
Reflexive: For any non-null reference value x, x.equals(x) must return true.
Symmetric: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.
For any non-null reference value x, x.equals(null) must return false.
Here’s a recipe for a high-quality equals method:
Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization but one that is worth doing if the comparison is potentially expensive.
Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.
Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, return true; otherwise, return false. If the type in Step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility.
For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, call the equals method recursively; for float fields, use the static Float.compare(float, float) method; and for double fields, use Double.compare(double, double). The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double values; While you could compare float and double fields with the static methods Float.equals and Double.equals, this would entail autoboxing on every comparison, which would have poor performance. For array fields, apply these guidelines to each element. If every element in an array field is significant, use one of the Arrays.equals methods.
Some object reference fields may legitimately contain null. To avoid the possibility of a NullPointerException, check such fields for equality using the static method Objects.equals(Object, Object).
// Class with a typical equals method
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
#Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}
#Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
When comparing objects in Java, you make a semantic check, comparing the type and identifying state of the objects to:
itself (same instance)
itself (clone, or reconstructed copy)
other objects of different types
other objects of the same type
null
Rules:
Symmetry: a.equals(b) == b.equals(a)
equals() always yields true or false, but never a NullpointerException, ClassCastException or any other throwable
Comparison:
Type check: both instances need to be of the same type, meaning you have to compare the actual classes for equality. This is often not correctly implemented, when developers use instanceof for type comparison (which only works as long as there are no subclasses, and violates the symmetry rule when A extends B -> a instanceof b != b instanceof a).
Semantic check of identifying state: Make sure you understand by which state the instances are identified. Persons may be identified by their social security number, but not by hair color (can be dyed), name (can be changed) or age (changes all the time). Only with value objects should you compare the full state (all non-transient fields), otherwise check only what identifies the instance.
For your Person class:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Reusable, generic utility class:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* #param instance object instance (where the equals() is implemented)
* #param other other instance to compare to
* #param stateAccessors stateAccessors for state to compare, optional
* #param <T> instance type
* #return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
For your Person class, using this utility class:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Since I'm guessing age is of type int:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
if age is int you should use == if it is Integer object then you can use equals().
You also need to implement hashcode method if you override equals. Details of the contract is available in the javadoc of Object and also at various pages in web.
tl;dr
record Person ( String name , int age ) {}
if(
new Person( "Carol" , 27 ) // Compiler auto-generates implicitly the constructor.
.equals( // Compiler auto-generates implicitly the `equals` method.
new Person( "Carol" , 42 )
)
) // Returns `false`, as the name matches but the age differs.
{ … }
Details
While your specific problem is solved (using == for equality test between int primitive values), there is an alternative that eliminates the need to write that code.
record
Java 16 brings the record feature.
A record is a brief way to write a class whose main purpose is to transparently and immutably carry data. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
equals method provided automatically
The default implicit equals method compares each and every member field that you declared for the record. The members can be objects or primitives, both types are automatically compared in the default equals method.
For example, if you have a Person record carrying two fields, name & age, both of those fields are automatically compared to determine equality between a pair of Person objects.
public record Person ( String name , int age ) {}
Try it.
Person alice = new Person( "Alice" , 23 ) ;
Person alice2 = new Person( "Alice" , 23 ) ;
Person bob = new Person( "Bob" , 19 ) ;
boolean samePerson1 = alice.equals( alice2 ) ; // true.
boolean samePerson2 = alice.equals( bob ) ; // false.
You can override the equals method on a record, if you want a behavior other than the default. But if you do override equals, be sure to override hashCode for consistent logic, as you would for a conventional Java class. And, think twice: Whenever adding methods to a record, reconsider if a record structure is really appropriate to that problem domain.
Tip: A record can be defined within another class, and even locally within a method.
Here is the solution that I recently used:
public class Test {
public String a;
public long b;
public Date c;
public String d;
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Test)) {
return false;
}
Test testOther = (Test) obj;
return (a != null ? a.equals(testOther.a) : testOther.a == null)
&& (b == testOther.b)
&& (c != null ? c.equals(testOther.c) : testOther.c == null)
&& (d != null ? d.equals(testOther.d) : testOther.d == null);
}
}
For lazy programmers: lombok library is very easy and time saving. please have a look at this link
instead of writing lines of codes and rules, you just need to apply this library in your IDE and then just #Data and it is Done.
import lombok.Data;
#Data // this is the magic word :D
public class pojo {
int price;
String currency;
String productName;
}
in fact in the above code, #Data is a shortcut for
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter
#Setter
#EqualsAndHashCode
#ToString
//or instead of all above #Data
public class pojo {
int price;
String currency;
String productName;
}
Here, I'll just post my code:
int len = InternalList.size();
ListIterator<E> forward = InternalList.listIterator( 0 );
ListIterator<E> backward = InternalList.listIterator( len );
while( forward.hasNext() && backward.hasPrevious() )
{
E next = forward.next();
E prev = backward.previous();
// When the object references are the same, we expect to be at the
// center of the list (for odd-numbered lists?); we're done
if( next == prev )
return true;
// Otherwise, if the object values aren't the same, we're not a
// palindrome
if( !((E)next).equals( prev ) )
return false;
}
And here's the internal list:
private LinkedList<E> InternalList;
So basically my problem is the last if statement only checks Object's equals() method; not the E's equals(). If forcibly casting it doesn't work, what does?
The runtime types of the elements returned from the iterators are not (and indeed cannot be) changed. They're assigned to fields of type E, which may well be erased to Object at runtime (depending on the generic bounds) but this won't affect the objects themselves.
When equals() is invoked, it's a non-static method and so is invoked on whatever the class of the next object happens to be. If this class doesn't have an overridden equals method then sure, the default Object.equals will be used. However, if this object's class directly or indirectly overrides equals, the most specific override will be used.
In other words, this code should be fine (and the cast is completely unnecessary).
I suggest that you double-check you've overridden equals correctly in the class in question. I would guess that you've implemented it as something like:
public class MyFoo {
...
public boolean equals(MyFoo other) {
...
}
}
whereas the argument must be of type Object, otherwise you're just overloading the equals method instead of overriding it. If you're using Java 6, you can add the #Override annotation to your method, which will catch this sort of error.
The correct implementation of equals(Object) will be chosen at runtime, due to runtime polymorphism. Why do you think that's not the case?
Actually, you might have made a common mistake and implemented equals(ASpecificType) instead of equals(Object): you want to override the equals(Object) method from java.lang.Object. Specifying a different parameter type means you no longer override that method.
A common equals() implementation for ASpecificType could start like this:
public boolean equals(Object o) {
if (this==o) {
return true;
} else if (o==null || o.getClass() != getClass()) {
return false;
}
ASpecificType other = (ASpecificType) other;
// insert specific comparison here
return result;
}
The casting casts E to E so it doesn't do anything.
equals should work without casting.
As you posted in the comment, next == prev will not work for even-numbered lists.
Concerning how to implement equals:
public boolean equals(Object o) {
if(this == o) { return true; }
if(o == null) { return false; }
if(o instanceof [ClassOfThis]) {
o = (Type)o;
// compare here.
} else {
return false;
}
}