Java equals() method for inherited objects - java

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

Related

What is the best practice for extracting these 2 classes to a common class?

I have two very simple classes. Both have 2 variables which are UUIDs, and I'm thinking about what the best approach is for extracting it into a common class.
Below are the classes I have defined:
#AllArgsConstructor
final class ObjectKey
{
#Getter
private UUID sourceId;
#Getter
private UUID targetId;
#Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
ObjectKey key = (ObjectKey) o;
return sourceId.equals(key.getSourceId()) && targetId.equals(key.getTargetId());
}
#Override
public int hashCode()
{
return (sourceId.hashCode() + targetId.hashCode()) * 31;
}
}
final class AnimalKey
{
#Getter
private UUID catId;
#Getter
private UUID dogId;
#Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
AnimalKey key = (AnimalKey) o;
return dogId.equals(key.getDogId()) && catId.equals(key.getCatId());
}
#Override
public int hashCode()
{
return (catId.hashCode() + dogId.hashCode()) * 31;
}
}
So I'm wondering what is the best practice, since both classes basically have the same number of variables with the same type... only that the variable names are different.
Of course one option is to have only 1 class that has generic names like firstID and secondID. But then the code using the getters/setters for these classes are not very easy to read and understand. Is there any obvious approach I'm missing?
I don't think there is anything as elegant as you are envisaging or not much of a necessity/benefit. However, a couple of options that come to mind are to have a common base class and some explicit getters that map to friendlier names and/or and object for a TupleUUID.
#AllArgsConstructor
final class TupleUUID
{
#Getter
private UUID primaryId;
#Getter
private UUID secondaryId;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TupleUUID that = (TupleUUID) o;
return Objects.equals(primaryId, that.primaryId)
&& Objects.equals(secondaryId, that.secondaryId);
}
#Override
public int hashCode() {
return Objects.hash(primaryId, secondaryId);
}
}
#AllArgsConstructor
class TupleKey {
#Getter(AccessLevel.PROTECTED)
private TupleUUID uuid;
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TupleKey that = (TupleKey ) o;
return Objects.equals(key, that.key);
}
#Override
public int hashCode() {
return uuid.hashCode();
}
}
#AllArgsConstructor
final class ObjectKey extends TupleKey {
public UUID getSourceId() {
return getUuid().getPrimaryId();
}
public UUID getTargetId() {
return getUuid().getSecondaryId();
}
}
#AllArgsConstructor
final class AnimalKey extends TupleKey {
public UUID getCatId() {
return getUuid().getPrimaryId();
}
public UUID getDogId() {
return getUuid().getSecondaryId();
}
}
Of course, you can mix and match this and get rid of the TupleKey base class or roll TupleUUID into TupleKey instead.
The benefits of this are questionable unless you are going to have a lot of objects with two unique identifiers.

Using Contains for objects in List collection

I'm trying to find an object in the List collection by his name using Contains method, but, somehow, it doesn't work. How should I use it?
This is how I try to use this
CandyDao.getAllCandys().contains("Caramel")
But it can't find an object which I need.
CandyDao.java
public class CandyDao {
private List<Candy> candys = Arrays.asList(new Candy("Caramel", 3, false),
new Candy("Marmelade", 2, true));
public List<Candy> getAllCandys(){
return candys;
}
}
Candy.java
public class Candy {
private String name;
private float price;
private boolean InStock;
public Candy() {
}
public Candy(String name, float price, boolean InStock) {
setName(name);
setPrice(price);
setInStock(InStock);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public boolean getInStock() {
return InStock;
}
public void setInStock(boolean InStock) {
this.InStock = InStock;
}
}
Since the list contains Candy objects, the contains() method needs a Candy object for the comparison, so you can't use contains("Caramel").
To check if the list contains a Candy object with a name of "Caramel", you can use Java 8+ Streams to do the search:
CandyDao.getAllCandys().stream().Map(Candy::getName).anyMatch("Caramel"::equals);
The equivalent non-stream version would be:
boolean hasCaramel = false;
for (Candy candy : CandyDao.getAllCandys()) {
if ("Caramel".equals(candy.getName())) {
hasCaramel = true;
break;
}
}
Override the equals & hashcode method like below :
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Candy candy = (Candy) o;
return Objects.equals(name, candy.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
Now, Since the equals function only checks name for equality of Candy object, the following should work:
CandyDao.getAllCandys().contains(new Candy("Caramel", 0, true)) . //2nd & 3rd arg of Candy constructor are inessential/dummy
You should override the Object#equals method in Candy as shown below:
#Override
public boolean equals(Object o) {
if (!(o instanceof Candy)) {
return false;
}
Candy that = (Candy) o;
return Objects.equals(that.getName(), this.getName());
}
After overriding, List#contains should return true if the name matches.
getAllCandys() method returns a list of Candyobjects. You should look each element's nameproperty on the list. So cis an element of candyslist. Use c.getName().contains("Caramel") to look for "Caramel"
for(Candy c : candys) {
System.out.println(c.getName() + " contains? " + c.getName().contains("Caramel"));
}

Java: HashSet multiple types

I have a program that I have to use a HashSet for. My question arises from the fact that HashSets mainly contain one object, but if I wish to send information to the other class, it takes three objects: one string, one int, and one boolean.
The assignment says that I must use a HashSet
Constructor I am trying to send information to:
public Magic (String name, int size, boolean isVisible)
I have a class that is supposed to be sending sets of spells containing name, size, and isVisible.
Magic.go() class:
public void go()
{
int i = 0;
while (i < size) {
if (isVisible == true) {
System.out.println(name + "!");
}
i++;
}
}
Just create an object which contains all the three fields like this:
import java.util.Objects;
public class NameSizeVisible {
private final String name;
private final int size;
private final boolean isVisible;
public NameSizeVisible(String name, int size, boolean isVisible) {
this.name = name;
this.size = size;
this.isVisible = isVisible;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public boolean isVisible() {
return isVisible;
}
#Override
public int hashCode() {
return Objects.hash(name,size,isVisible);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameSizeVisible other = (NameSizeVisible) obj;
if (isVisible != other.isVisible)
return false;
if (!Objects.equals(name, other.name))
return false;
if (size != other.size)
return false;
return true;
}
}
You can use a HashSet that stores Objects. So you would have:
HashSet<Object> set = new HashSet<>();
set.add(name);
set.add(size);
set.add(isVisible);
Then when you access the objects you just need to cast them to their respective types:
String name = "";
int size = 0;
boolean isVisible = false;
for (Object o : set) {
if (o instanceof String) {
name = (String) o;
} else if (o instanceof int) {
size = (int) o;
} else {
isVisible = (boolean) o;
}
}

How to override equals() method

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.

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

Categories

Resources