1

I created an abstract class Fruit, which overrides the equals() method. Then I created a subclass, Orange, which overrides the copy() and the equals() method. In my test file, TestFruit.java, I am creating an array of oranges and testing their methods. I am trying to create a deep copy of orange and do a deep comparison between the parent orange and the copy. However, in my output, the comparison always returns false. I checked the parent and the copy's attributes and they do seem to be the same. Any pointers would be appreciated. I am pretty new to Java and copying. I attached my code below.

Fruit.java:

package juicer;

import copy.Copyable;

public abstract class Fruit implements Copyable, Cloneable
{
    private double mass;
    private boolean isJuicedRemoved;

    protected Fruit(double theMass)
            throws IllegalMassException
    {
        {
            if (theMass <= 0)
            {
                throw new IllegalMassException(theMass);
            }
            else
            {
                this.mass = theMass;
                this.isJuicedRemoved = false;
            }
        }
    }

    protected Fruit(Fruit fruit)
    {
        this.mass = fruit.mass;
        this.isJuicedRemoved = fruit.isJuicedRemoved;
    }

    public double getMass()
    {
        return mass;
    }

    public boolean getIsJuicedExtracted()
    {
        return isJuicedRemoved;
    }

    protected void setMass(double value)
    {
        this.mass = value;
    }

    protected abstract double juiceRatio();

    public double extractJuice()
    {
        double liquidMass = amountJuice();

        if (!isJuicedRemoved)
        {
            isJuicedRemoved = true;
            mass -= liquidMass;
        }

        return liquidMass;
    }

    public double amountJuice()
    {
        if (isJuicedRemoved) return 0.0;

        return mass * juiceRatio();
    }

    @Override
    public boolean equals(Object obj)
    {
        // Steps to override the equals() method():

        // Step 1: Test if obj is an instance of Fruit.
        //         If it is not, then return false.
        if (!(obj instanceof Fruit)) return false;

        // Step 2: Cast obj to an Fruit.
        Fruit rhs = (Fruit)obj;

        // Step 3: Test if the data fields of the invoking object are
        //         equal to the ones in rhs using a deep comparison
        //         and return this result.
        return super.equals(obj) && // test for equality in the super class
                mass == rhs.mass &&
                isJuicedRemoved == rhs.isJuicedRemoved;
    }

    @Override
    public int hashCode()
    {
        int result = super.hashCode();
        result = 31*result + Double.hashCode(mass);
        result = 31*result + Boolean.hashCode(isJuicedRemoved);
        return result;
    }

    @Override
    public Object clone() throws CloneNotSupportedException
    {
        Fruit objectClone = (Fruit)super.clone();

        objectClone.mass = mass;
        objectClone.isJuicedRemoved = isJuicedRemoved;

        return objectClone;
    }

    @Override
    public String toString()
    {
        return "\tmass = " + mass +
                "\n\tisJuiceExtracted = " + isJuicedRemoved + "\n";
    }
}

Orange.java:

package juicer;

public class Orange extends Fruit
{
    public Orange(double mass)
    {
        super(mass);
    }

    // copy constructor
    public Orange(Orange other)
    {
        super(other);
    }

    @Override
    protected double juiceRatio()
    {
        return 0.87;
    }

    @Override
    public boolean equals(Object obj)
    {
        // Steps to override the equals() method():

        // Step 1: Test if obj is an instance of Orange.
        //         If it is not, then return false.
        if (!(obj instanceof Orange)) return false;

        // Step 2: Cast obj to an Orange.
        // This step is not needed since the only data fields this
        // class has are the ones it inherits.

        // Step 3: Test if the data fields of the invoking object are
        //         equal to the ones in rhs using a deep comparison
        //         and return this result.
        return super.equals(obj);
    }

    @Override
    public Object copy()
    {
        return new Orange(this);
    }

    @Override
    public String toString()
    {
        return "Orange:\n" + super.toString();
    }
}

TestFruit.java:

package test;

import juicer.*;
import java.util.Random;

public class TestFruit
{
    public static void main(String[] args)
    {
        Orange[] oranges = new Orange[1];

        //Random double generator for mass
        Random rd = new Random();

        //create oranges
        for (int i = 0; i <= oranges.length - 1; i++ )
        {
            oranges[i] = new Orange(rd.nextDouble());
        }

        for (Orange orange : oranges)
        {
            Orange orangeCopy = new Orange(orange);

            if (orange == orangeCopy)
            {
                System.out.print("The comparison is true!");
            }
            else
            {
                System.out.print("Does not match.");
            }
        }

    }
}

1 Answers1

2

One of the common misconceptions in Java is the use of == vs .equals(). When you use == to compare two objects in Java, internally it's comparing its memory address. == does not actually call .equals().

In this case, you have two distinct orange objects, so the comparison will always return false.

If you use a.equals(b), then it will actually invoke your equals method which you implemented.

As @Andreas pointed out in the comments, there's another issue. Calling super.equals(obj) in Fruit will call the superclass implementation of equals, and the superclass of Fruit is Object. Object.equals() behaves the same as == (i.e. also checking for reference equality). Overriding .equals() is not trivial, so it can often be nice to have the IDE generate it for you.

In contrast with a language like C++, Java does not have operator overloading. This means that you can't define a different implementation for ==. This is why it's best practice to always call .equals() when comparing any non-primitive types (unless you're explicitly checking reference equality, which is rare).

rb612
  • 5,280
  • 3
  • 30
  • 68
  • Of course, since the `equals()` implementation erroneously calls `Object.equals()`, the result will be the same as using `==`. – Andreas Mar 05 '21 at 04:30
  • @Andreas, ah, good point—yes, `super.equals(obj)` in the `Fruit` superclass will call `Object.equals()` which will compare based on object identity as well. – rb612 Mar 05 '21 at 04:32