Comparing objects exercise (Java) - java

hopefully this doesn't make me seem to be an idiot but I seem to be failing on a simple exercise where I have to compare two objects to check if they are equal, my Java class is below along with the error message I'm getting from the exercise. Would anyone know how to solve it? Thanks in advance.
import java.util.Objects;
public class Person {
private String name;
private SimpleDate birthday;
private int height;
private int weight;
public Person(String name, SimpleDate birthday, int height, int weight) {
this.name = name;
this.birthday = birthday;
this.height = height;
this.weight = weight;
hashCode();
}
public String getName(){
return this.name;
}
public SimpleDate getBirthday(){
return this.birthday;
}
public Integer getHeight(){
return this.height;
}
public Integer getWeight(){
return this.weight;
}
// implement an equals method here for checking the equality of objects
#Override
public boolean equals(Object compared){
return this==compared;
}
}
Error message

Joshua Bloch in Effective Java gives guidelines on how to write a nice .equals(). Here's the excerpt directly from the book:
Use the == operator to check if the argument is a reference to this object.
Use the instanceof operator to check if the argument has the correct type.
Cast the argument to the correct type.
For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object.
When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(!(o instance of Person)) {
return false;
}
//you comparing logic here
}
You have to make sure that equals follows its contract (it's an equivalence relation). See it's documentation for more details. Also, override the hashcode() method.
You equals method is written wrongly as it just compares the location of objects in memory. That's why your tests are failing.

You changed behaviour of equals to == here:
#Override
public boolean equals(Object compared){
return this==compared;
}
and now here is already an answer - https://stackoverflow.com/a/13387787/7505731

Related

Why this Java Code is compiling successfully

I haven't overridden much of hashCode() and equals() methods so I may be wrong
My question is for the last line where
dep1.equals(emp2) is being compiled successfully(why) (I am expecting compilation error as they have different types) and after compiling I get following
15 15 false
where I am expecting 15 15 true since I am checking the hashcode in the equals method.
class Employee {
private String name;
private int id;
public Employee(String name, int id) {
this.name = name;
this.id = id;
}
public int hashCode() {
return this.id;
}
public boolean equals(Employee employee) {
return this.hashCode() == employee.hashCode();
}
public int getEmployeeId() {
return this.id;
}
}
class Department {
private String name;
private int id;
public Department(String name, int id) {
this.name = name;
this.id = id;
}
public int hashCode() {
return this.id;
}
public boolean equals(Department department) {
return this.hashCode() == department.hashCode();
}
public int getDepartmentId() {
return this.id;
}
}
public class JavaCollections {
public static void main(String args[]) {
Employee emp2 = new Employee("Second Employee", 15);
Department dep1 = new Department("Department One", 15);
System.out.println(dep1.hashCode()+" "+emp2.hashCode()+" " + dep1.equals(emp2));
}
}
First, for the reason why this compiles: all classes in Java inherit from java.lang.Object, which defines equals(Object) method, and provides a default implementation. This is the method that you call when you compare an Employee and a Department, not one of the overloads that you have provided.
Your equals code compiles fine, because the compiler does not know that you thought you were overriding equals when you actually didn't. The compiler thinks that you want to make a new method
public boolean equals(Department department)
to compare Department objects to other Department objects.
If you are writing a code that overrides a method of a superclass, add #Override annotation to it, like this:
#Override
public boolean equals(Department department)
Now the compiler will correctly complain to you that your method does not in fact override a method in its base class, alerting you to the problem at compile time.
To fix your code change the signatures of equals to take Object, add #Override, check for null and for the correct type, do the cast, and then do the actual comparison:
#Override
public boolean equals(Department obj) {
if (obj == null || !(obj instanceof Department)) {
return false;
}
Department dept = (Department)obj
return dept.id == id;
}
Note: Implementing equals like this
return this.hashCode() == department.hashCode();
is very fragile. Although it works in your case, when hash code is a unique ID of the object, this wouldn't survive a code refactoring when hashCode is replaced with some other implementation, for example, an implementation that considers both id and name. If you want to rely on comparing IDs, compare IDs directly, without calling hashCode to get them.
That's because both of classes Employee and Department still have not overriden methods public boolean equals(Object obj) inherited from Object class.
Exactly this method is invoked in dep1.equals(emp2), not public boolean equals(Department department).
More specifically, read JLS:
An instance method mC declared in or inherited by class C, overrides from C another method mA declared in class A, iff all of the following are true:
...
The signature of mC is a subsignature (§8.4.2) of the signature of mA.
In this case boolean equals(Department department) is not subsignature of boolean equals(Object obj).
First, this code dep1.equals(emp2) calls default implementation of Object class.
Second, U didnt overrides the default implementation in both of your class becoz u cant override equal method for specific customizied types.
If u need ur answer to be 15 15 true
replace
public boolean equals(Department department) {
return this.hashCode() == department.hashCode();
}
by
#override
public boolean equals(Object department) {
return this.hashCode() == department.hashCode();
}

why i cant remove the object with override

In the collection test, I create a class named Name and override the equals method, like following,
class Name implements Comparable<Name>{
private String firstName, lastName;
Name(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public String toString(){
return firstName + " "+lastName;
}
public boolean equals(Name name){
return firstName.equals(name.firstName) && lastName.equals(name.lastName);
}
public int hashCode(){
return firstName.hashCode();
}
But When I test the remove() function in collection, it was false and the Name("Andy","Light") is still in the collection. What is the wrong with my code?
public static void main(String[] args){
Collection c = new HashSet();
c.add("hello");
c.add(new Name("Andy","Light"));
c.add(new Integer(100));
c.remove("hello");
c.remove(new Integer(100));
System.out.println(c.remove(new Name("Andy","Light")));
System.out.println(c);
}
There is a comment and an answer that says your hashCode() method is not consistent with equals() because you didn't include lastName in the hash code calculation. They are both wrong.
The hashCode() implementation is allowed to use a subset of the values used by equals(). It will cause more hashcode collisions that way, offsetting improved speed of hashCode() vs degraded performance of hash-buckets. A subset hashcode may be ok, it depends on likelihood of Name objects having same firstName.
Your problem is that the signature of equals() is wrong. It has to be boolean equals(Object).
boolean equals(Name) is not an override of boolean equals(Object), so you didn't actually override/implement the equals() method, and as such ended up with hashCode() being inconsistent with equals() (but not for the reason the others said).
If you add the #Override annotation, the compiler would have caught this problem. Always use the annotation.
Change to:
#Override
public boolean equals(Object obj) {
if (! (obj instanceOf Name))
return false;
Name that = (Name)obj;
return this.firstName.equals(that.firstName) && this.lastName.equals(that.lastName);
}
#Override
public int hashCode() {
return this.firstName.hashCode();
}
This of course assumes that neither can be null.
As #MickMnemonic says in a comment:
It's considered bad practice to leave out fields that are included in equals()
To include lastName in the calculation, use Objects.hash():
#Override
public int hashCode() {
return Objects.hash(this.firstName, this.lastName);
}
Also, as #StephenB said in a comment:
You also need to add a compareTo method because you are implementing Comparable<Name>.
Here you use Name as a parameter, not Object, because of the generic type argument to Comparable.
Example (if sorting by first name before last name):
#Override
public int compareTo(Name that) {
int cmp = this.firstName.compareTo(that.firstName);
if (cmp == 0)
cmp = this.lastName.compareTo(that.lastName);
return cmp;
}
That implements a firstName/lastName lexicographical ordering. You may want to use compareToIgnoreCase() or maybe a Collator for correct localized ordering.

Whether I need to define my own hash and equal method?

If I need to use InfoName as the key of a HashMap, do I need to define my own hashCode() and equals() method? I think it's not necessary, since the String name variable will be enough to make sure each object of InfoName is different.
public class InfoName {
enum Type {
company, product;
}
public String name;
public Type type;
public InfoName(String name, Type type) {
this.name = name;
this.type = type;
}
}
the String "name" variable will be enough to make sure each object of
InfoName is different
If you only want to use the name in the InfoName, then just make String type name as the key as it already override equals() and hashCode() .
OR
You need to override equals() and hashCode() in InfoName class, else how would JVM knows on which attribute/criteria you are using for hashing and equality check.
If you are sure to have InfoName as key you need to override both.
You an have something like
public class Test {
enum Type {
company, product;
}
public String name;
public Type type;
public Test(String name, Type type) {
this.name = name;
this.type = type;
}
#Override
public boolean equals(Object o) {//or do what you like
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
if (!name.equals(test.name)) return false;
if (type != test.type) return false;
return true;
}
#Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + type.hashCode();
return result;
}
}
Basically your editor provides features of overriding hashcode and equals. Have a look here Why do I need to override the equals and hashCode methods in Java?enter link description here
I think it's not necessary, since the String "name" variable will be
enough to make sure each object of InfoName is different.
I would recommend not to use name as the hash key because it seems a bad candidate. I mean could you have multiple objects with the same product name? In that case you would have many collisions.

Trouble implementing the comparable interface

I keep getting the error: Student is not abstract and does not override abstract method compareTo(java.lang.Object) in java.lang.Comparable
Why is this? What this is trying to accomplish is taking a list of students and comparing them by GPA.
public class Student implements Comparable
{
private String name;
private double gpa;
public Student(String name, double gpa)
{
this.name = name;
this.gpa = gpa;
}
public String getName()
{
return name;
}
public double getGpa()
{
return gpa;
}
public String toString()
{
return "Name: " + name + " GPA: " + gpa;
}
public double compareTo(Object other)
{
Student filler = (Student)other;
if(this.getGpa() < filler.getGpa())
return -1;
else if(this.getGpa() == filler.getGpa())
return 0;
else
return 1;
}
}
To answer your question directly, you need to change the return type of compareTo() from double to int.
There are also several other modifications you should make to improve your code:
implement Comparable<Student> instead of just Comparable. This makes it so you can write public int compareTo(Student other) and only allows calling compareTo() with other Student references.
Add #Override annotations before both toString() and compareTo(). This annotation helps you avoid some common errors which the compiler cannot catch.
compareTo method returns an int and not a double.
Also using an Override annotation helps to be sure that you are overriding the method correctly. So change this
public double compareTo(Object other)
to
#Override
public int compareTo(Object other)
public double compareTo(Object other)
should be
#Override
public int compareTo(T other)
Take a look at the Comparable interface. It requires a method called compareTo, that takes an argument of type T (generic parameter), that returns an int. The method you have created doesn't implement the method specified in the interface, which is why the Java compiler is complaining.
Since the Comparable interface is genericized, you should take advantage of generics and make your class implement Comparable<Student>. When you do that, the signature of compareTo becomes:
#Override
public int compareTo(Student other)
Which is better than a raw Object, since you don't have to cast, and more importantly, you don't accidentally end up passing in something that is not a Student.
One more thing: use the #Override annotation when you implement methods from the interface. Assuming you're using a halfway-decent IDE, you would have seen an error if you had:
#Override
public double compareTo(Object other)
Since there is no method with that signature in the interface.

Why isn't Collections.binarySearch() working with this comparable?

I have this Player class which implements the Comparable interface. Then I have an ArrayList of Players. I'm trying to use binarySearch() on the list of Players to find one Player, but Java is giving me a "cannot find symbol: method binarySearch(java.util.ArrayList< Player>,Player)".
This the Player class:
class Player implements Comparable {
private String username;
private String password;
Statistics stats;
//Constructor, creates a new Player with a supplied username
Player(String name) {
username = name;
password = "";
stats = new Statistics();
}
//Accessor method to return the username as a String
String getName() {
return username;
}
String getPassword() {
return password;
}
void setPassword(String newPass) {
password = newPass;
}
//Method to change the username
void setName(String newName) {
username = newName;
}
public int compareTo(Object o) {
return username.compareTo(((Player)o).username);
}
}
Weird thing, when I try Collections.sort() on this same list, it works.
Use are using generics inconsistently. Take heed of the compiler warnings. Always supply generic arguments (or never supply them).
Instead of:
class Player implements Comparable {
[...]
public int compareTo(Object o) {
Use
class Player implements Comparable<Player> {
[...]
public int compareTo(Player o) {
The rules of generics are difficult enough without the complication of rare types. So, typically the language spec gives up if you mix them up.
As long as you are implementing Comparable, you can make compareTo() consistent with equals() by also overriding equals() and hashCode(). This is particularly easy in this case, as you can simply delegate to String. Moreover, it's convenient if you ever need a Map containing instances of Player:
class Player implements Comparable<String> {
private String username;
private String password;
// ...
#Override
public int compareTo(String name) {
return username.compareTo(name);
}
#Override
public boolean equals(Object obj) {
return obj instanceof Player
&& username.equals(((Player)obj).username);
}
#Override
public int hashCode() {
return username.hashCode();
}
}

Categories