How to override equals() method - java

public class Car
{
private String name;
public int id;
public Car(String name, int id)
{
this.name = name;
this.id = id;
}
#Override
public boolean equals(Object ob)
{
if (!(ob instanceof Car))
{
return false;
}
Car that = (Car)ob;
return this.id == that.id;
}
#Override
public int hashCode()
{
return id;
}
// this class also got getters and setters
Then I got another class
public class CarList
{
private Collection<Car> cars;
public CarList()
{
cars = new HashSet<>();
}
public boolean insertCar(Car car)
{
return cars.add(car);
}
My question is: How to properly override equals() and hashCode() method, where I consider 'id' and 'name' attribute for object comparsion and hashCode calculation ( so there is no possibility to have 2 objects with the same name and ID - because in this code as it is - it only takes 'id' attribute for object comparsion)?

As of Java 7, there are static methods on Objects that makes implementing hashCode and equals easier. This should work well, assuming you don't want to use getClass() instead of instanceof to determine type compatibility. That depends on how subclasses of Car should compare to Cars.
#Override
public boolean equals(Object ob)
{
if (!(ob instanceof Car))
{
return false;
}
Car that = (Car)ob;
return Objects.equals(this.id, that.id) && Objects.equals(this.name, that.name);
}
#Override
public int hashCode()
{
return Objects.hash(id, name);
}

Instead of using
if (!(ob instanceof Car))
{
return false;
}
You should think about using
if (getClass() != obj.getClass())
return false;
Lets assume you have ForWdCar extends Car and TwoWdCar extends Car with equal name and id.
Do you want them to be equal? 1st solution,
Do you want them to be unequal? 2nd solution
You don't care, such cases don't happen? Second solution, it's faster.

Related

Using an interface as a key in a hashmap

I tried to use an interface as a key in a hashMap in order to have 1 map for multiple types of keys. The following seems to work.
interface Foo {
void function();
}
static class Bar implements Foo {
private int id;
public Bar(int id) {
this.id = id;
}
#Override
public void function() {
System.out.println("this is bar");
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bar bar = (Bar) o;
return id == bar.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
public static Map<Foo, Integer> map = new HashMap<>();
static class Baz implements Foo {
String name;
public Baz(String name) {
this.name = name;
}
#Override
public void function() {
System.out.println("this is Baz");
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Baz baz = (Baz) o;
return name.equals(baz.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
}
public static void main(String[] args) {
Bar bar = new Bar(123);
Baz baz = new Baz("some name");
map.put(bar, 10);
map.put(baz, 20);
System.out.println(map.get(bar));
}
What I am not sure about is if there is some corner case that would break this map?
Is there a case that having an interface as a key would break down? Could I have done it simpler using generics?
The only thing that's slightly out of the ordinary is that the equals methods have to compare Bar and Baz objects. When a Map only has one type of objects, the check this.getClass() == that.getClass in equals method never returns false. You have implemented this correctly though, so you don't have anything to worry about.
You may get more hash collisions than you expect. Imagine you have two classes that both have an int id field and implement hashCode with Objects.hash(id) - now objects of different classes with the same ID have the same hash code. If this use case is expected, you can perturb the hash in a way unique to each class, for example by mixing a class-specific constant to the hash:
#Override
public int hashCode() {
return Objects.hash(1, id);
}
...
#Override
public int hashCode() {
return Objects.hash(2, name);
}
In theory there could be problems with potentially more hash collisions leading to bad performance due to different implementations of hashCode, so you need to be careful, and test it with the real data. Other than that it is a valid use case.

Java equals() method for inherited objects

Here is my code:
class Car {
Piece doors;
Piece window;
String name;
List<Sign> signs; //???
Car() {
}
#Override
public String toString() {
//...
}
#Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != this.getClass()) {
return false;
}
if (this.doors == other.doors && this.window == other.window ) {
return true;
}
return false;
}
}
class Piece extends Car {
Piece() {
//...
}
}
class Brand extends Car {
Brand(String name, Sign[] signs) {
this.name = name;
this.signs = signs;
}
List<Sign> signs() {
return this.signs;
}
}
This is my structure of my classes. Now, I would like to find out, whether two compared objects are equal or not. Let's say we have two Piece objects. How can I compare them in method Car.equals()? The problem here is how to find whether that object belongs to Piece or Brand subclass.
Piece should not extend Car, but Car should be composed of Piece objects such as Window, Door, etctera. A Piece should implement equals/hashcode. Although having the interface explicitly define those functions doesn't mean the inherited class requires it, it still hints that it is necessary (refer to Comparator).
The equals contract requires the referenced members to be final for it to meet the contract. Brand overrides equals/hashcode to compare the brand name.
Window and Door need defining final members to make them truly unique but unfortunately none are provided in the question.
static class Car {
private final Piece doors;
private final Piece window;
private final Brand brand;
private final int hashCode;
Car(Piece doors, Piece window, Brand brand) {
this.doors = doors;
this.window = window;
this.brand = brand;
this.hashCode = Objects.hash(doors, window, brand);
}
#Override
public int hashCode() {
return hashCode;
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Car) {
Car otherCar = (Car) obj;
return otherCar.doors.equals(doors) && otherCar.window.equals(window) && otherCar.brand.equals(brand);
}
return false;
}
}
static class Window implements Piece {
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
static class Door implements Piece {
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
interface Piece {
boolean equals(Object other);
int hashCode();
}
static class Brand {
private final String name;
private final int hashCode;
Brand(String name) {
this.name = name;
this.hashCode = Objects.hash(name);
}
#Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other instanceof Brand) {
Brand otherBrand = (Brand) other;
return otherBrand.name.equals(name);
}
return false;
}
#Override
public int hashCode() {
return hashCode;
}
}

How to create an immutable class

I want to make the class below immutable. Can anyone provide a simple example of creating an immutable class in java?
class Emp implements Comparable
{
String name,job;
int salary;
public Emp(String n,String j,int sal)
{
name=n;
job=j;
salary=sal;
}
public void display()
{
System.out.println(name+"\t"+job+"\t"+salary);
}
public boolean equals(Object o)
{
// use a shortcut comparison for slightly better performance; not really required
if (this == o)
{
return true;
}
// make sure o can be cast to this class
if (o == null || o.getClass() != getClass())
{
// cannot cast
return false;
}
// can now safely cast
Emp p=(Emp)o;
return this.name.equals(p.name)&&this.job.equals(p.job) &&this.salary==p.salary;
}
public int hashCode()
{
return name.hashCode()+job.hashCode()+salary;
}
public int compareTo(Object o)
{
Emp e=(Emp)o;
return this.name.compareTo(e.name);
//return this.job.compareTo(e.job);
// return this.salary-e.salary;
}
}
Just label all fields of your class as final, and don't assign to them anywhere but the constructor for your class.
Also, it's good to make the class final, or to only provide private constructors, and static factory methods. This means people cannot subclass your class and override you methods.
for example:
public class Immutable {
private final String value;
private Immutable(String value) {
this.value = value;
}
public static Immutable create(String value) { return new Immutable(value); }
public String getValue() { return value; }
}

How to define multiple equals() function for a class

I want to override "public boolean equals(Object obj)" function, for name and age, in my class named MyObject whose structure is given below
public class MyObject{
private String name;
private int age;
}
How can i ?
#balusC :
What about this ?
vo = new MyObject() {
public boolean equals(Object obj) {
return ((MyObject)obj).name().equals(this.getName());
}
vo = new MyObject() {
public boolean equals(Object obj) {
return ((MyObject)obj).age() == (this.getAge());
Your question is a bit vague, but if the sole purpose is to have different sorting algorithms depending on what property you'd like to use, then rather use a Comparator.
public class Person {
private String name;
private int age;
public static Comparator COMPARE_BY_NAME = new Comparator<Person>() {
public int compare(Person one, Person other) {
return one.name.compareTo(other.name);
}
}
public static Comparator COMPARE_BY_AGE = new Comparator<Person>() {
public int compare(Person one, Person other) {
return one.age > other.age ? 1
: one.age < other.age ? -1
: 0; // Maybe compare by name here? I.e. if same age, then order by name instead.
}
}
// Add/generate getters/setters/equals()/hashCode()/toString()
}
which you can use as follows:
List<Person> persons = createItSomehow();
Collections.sort(persons, Person.COMPARE_BY_NAME);
System.out.println(persons); // Ordered by name.
Collections.sort(persons, Person.COMPARE_BY_AGE);
System.out.println(persons); // Ordered by age.
As to the actual equals() implementation, I'd rather let it return true when the both Person objects are techically or naturally identical. You can use either a DB-generated PK for this to compare on technical identity:
public class Person {
private Long id;
public boolean equals(Object object) {
return (object instanceof Person) && (id != null)
? id.equals(((Person) object).id)
: (object == this);
}
}
or just compare every property to compare on natural identity:
public class Person {
private String name;
private int age;
public boolean equals(Object object) {
// Basic checks.
if (object == this) return true;
if (object == null || getClass() != object.getClass()) return false;
// Property checks.
Person other = (Person) object;
if (name == null ? other.name != null : !name.equals(other.name)) return false;
if (age != other.age) return false;
// All passed.
return true;
}
}
Don't forget to override hashCode() as well when you override equals().
See also:
Object ordering
Sorting an ArrayList of objects
Overriding equals() and hashCode()
I'm not exactly sure what you're aiming at with this. The general expectation of equals() is that it returns false for null and objects of other classes and performs value equality on the relevant fields of the class in question.
While you can certainly handle String and Integer in the following way:
public boolean equals(Object o) {
if (o == null) return false;
if (o instanceof String) return name.equals(o);
if (o instanceof Integer) return ((Integer)o) == age;
...
}
this breaks the contract for equals so you can't do it (except not without things going wrong in very weird ways).
equals is an equivalence relation, so it has to be reflexive, symmetric and transitive. The symmetric part here is key, since if a.equals(b) then b.equals(a). Both String and Integer won't do that for you.
If you want just helper functions that check whether the name or the age is equals to a given name/age, then you can do that without using equals():
public boolean equalsName(String name) { return name.equals(this.name); }
public boolean equalsAge(int age) { return age == this.age; }
Just keep it short and simple (aka KISS principle): write setters and getters. Something like in the following example:
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
And then in the method you need to do the check you can write:
Person person = new Person();
if(person.getName().equals("Something")) doThis();
if(person.getAge() == 1337) doThat();
Not sure what you mean by "multiple equals()". If you want compare both your fields, you just need to override the equals method like this,
public boolean equals( Object o )
{
if ( o != null && o instanceof MyObject )
{
MyObject m = (MyObject) o;
if (this.name == null)
return false;
return this.name.eqauls(m.name) && this.age == m.age;
}
return false;
}
/// Compute a hash code for the pair.
public int hashCode()
{
int code = name == null ? 0 : name.hashCode();
return code ^ age;
}
It's a good practice to change hashCode whenever you change equals so HashMap works efficiently with your object.
if you do want to override equals, it should look something like this:
static private <T> boolean checkEquals(T t1, T t2)
{
return (t1 == null) ? (t2 == null) : t1.equals(t2);
}
#Override public boolean equals (Object o)
{
if (o instanceof MyObject)
{
MyObject obj = (MyObject)o;
return checkEquals(this.name, obj.getName())
&& this.age == o.getAge();
}
else
return false;
}
#Override public int hashCode()
{
// implement hashCode
}
You need to override both hashCode() and equals() or neither. And you also should make sure your class is final, otherwise there are potential pitfalls with equals.
public class MyObject {
private String name;
private int age;
#Override
public boolean equals(Object o){
if(o instanceof MyObject){
MyObject otherObject = (MyObject)o;
if(name == null){
return otherObject.name == null && otherObject.age == age;
} else {
return name.equals(otherObject.name) && otherObject.age == age;
}
} else {
return false;
}
}
// When we overriding equals it is a good practice to override hashCode
// for consistecy
#Override
public int hashCode(){
int nameCode = (name == null) ? 0 : name.hashCode();
// See Item 9 in book Effective Java 2nd Edition
return 31 * nameCode + age;
}
}

How can I override equals() method using inheritance?

I have a subclass called "worker" extending the "Person" class. I am trying to override the equals() method from "Person" within the subclass of "Worker". Can anyone explain if my attempt is correct in terms of a basic override?
public class Person {
private String name;
public Person(String n) {
name = n;
}
public Person() {
this("");
}
public String getName() {
return name;
}
public String toString() {
return getName() + " ";
}
public boolean equals(Object rhs) {
if (!(rhs instanceof Person)) {
return false;
}
Person other = (Person) rhs;
return this.getName().equals(other.getName());
}
class Employee extends Person {
double salary;
public Employee(double b) {
salary = b;
}
Employee() {
salary = 150000;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return super.toString();
}
// my attempt
#Override
public boolean equals(Object rhs) {
if (rhs == null) {
return false;
}
if (getClass() != rhs.getClass()) {
return false;
}
if (!super.equals(rhs))
return false;
else {
}
return false;
}
}
}
You've sort of mechanically handled the override part correctly, but the equals method of your Employee class will never return true.
First because of this:
if (!super.equals(rhs)) return false; else { }
You'll always fall through to the final return false, even if the result of super.equals is true.
Once you fix that, you still have a problem with the equals method of Person. If you pass in an instance of Worker, that equals method will always return false.
There may yet be other things, but those two are show stoppers.
The NetBeans using the shortcut alt + insert, you can automatically generate the equals method, constructors, getters, setters, delegate method, and others. If you want to use the collection using encoding mixing(Hashtable, HashMap, HashSet) it with redefining equals you must also redefine the hashCode().

Categories

Resources