I'm writing a program that compare two objects'distance. I already implements the comparable class to my distance. I also override the compareTo() method. So far it does not give me a compiler error, but I have a logic error. I have been try to search where my error occur, but I still cannot find it.

Here is the code:

//Distance class with feet and inches

public class Distance implements Comparable
{
	private int feet;
	private int inches;
	private final int DEFAULT_FT = 1;
	private final int DEFAULT_IN = 1;

	//constructors	
	public Distance()
	{
		feet = DEFAULT_FT;
		inches = DEFAULT_IN;
	}

	public Distance( int ft, int in )
	{
		feet = ft;
		inches = in;
	}
	
	//get methods
	public int getFeet()
	{
		return feet;
	}
	
	public int getInch()
	{
		return inches;
	}
	//set methods - validate + whole integers only
	public void setFeet(int ft)
	{
		feet = ft;
	}
	
	public void setInch(int in)
	{
		inches = in;
	}
	//method to add two distance objects
	
	public Distance add(Distance k)
	{
		Distance p = new Distance();
		p.inches = this.inches+ k.inches;
		p.feet = this.feet+ k.feet;
		
		while(p.inches>12)
		{
			p.inches = p.inches-12;
			p.feet++;
		}

		return p;

	}
	//method to subtract two distance objects
	
	public Distance sub(Distance d)
	{
		Distance k = new Distance();
		k.inches = this.inches- d.inches;
		k.feet = this.feet- d.feet;
		while(k.inches<0)
		{
			k.inches = k.inches+12;
			k.feet--;
		}

		return k;	
	}
	//equals() - compares two distance objects for equality
	@Override
	public boolean equals(Object obj)
	{
		if( obj == null)
			return false;
		if(! (obj instanceof Distance) )
			return false;	
			Distance d = (Distance)obj;
		if( this.feet == d.feet && this.inches == d.inches)
			return true;
		else		
		return false;

	}
	//hashCode() - same hash code for equal objects
	@Override
	public int hashCode()
	{
		return ((Integer)feet).hashCode()+inches;
	}
	//compareTo() - compares two distance objects ( <, >, == )
	@Override
	public int compareTo(Object obj)
	{
		Distance k = new Distance();
		Distance k1 = new Distance();
		k1 = (Distance) obj;
		if (k.getFeet() > k1.getFeet() )
		{
			return 1;
		}
		else if( k.getFeet() == k1.getFeet() )
		{
			if (k.getInch() > k1.getInch() )
			{
				return 1;
			}
			else if (k.getInch() == k1.getInch() )
			{
				return 0;
			}
			else
			{
				return -1;
			}
			
		}
		else
			return -1;
	}
	
	@Override
	public String toString()
	{
		return String.format("%d' %d\"", feet, inches );
	}
	
}

Thank you so much

I don't have a main class yet. I just create a TestCase class to test each method. But this is my test method:

protected void setUp()
	{
		d1 = new Distance( 1, 2);
		d2 = new Distance( 2, 5);
	}
	public void testCompareToLess()
	{
		assertEquals(-1 ,d1.compareTo(d2));
	}
	
	public void testCompareToGreater()
	{
		d2.setFeet(1);
		d2.setInch(2);
		assertEquals(0, d1.compareTo(d2));
	}

Forgot to say, the testcase output is expect 0, but return -1 for testCompareToGreater

Your `compareTo` method isn't proper. In compareTo, you don't create "new" objects but compare the object on which the method was invoked with the object which was passed in. Your current code doesn't work because "k" should ideally be the "this" object. Also, your assignment to "k1" is lost/meaningless since you again reassign with the cast of "obj".

Also, make sure you use `Comparable<T>` where T is the type you are comparing instead of simply Comparable (given that Comparable is a generic interface). This will help you get rid of the cast.

Your hashCode() can be made a bit better by following the code which is used by most hashCode() implementations of the standard library. Something like:

@Override
public int hashCode() {
    int prime = 31, result = 1;
    result = prime * result + ft;
    result = prime * result + in;
    return result;
}

Your equals() method is missing a inexpensive check to make sure that if equals() is invoked on the same object, it returns without checking anything.

public boolean equals(Object that) {
  if (that == null) return false;
  if (this == that) return true; // -> missing this
  // remaining code
}

Edited 4 Years Ago by ~s.o.s~: n/a

A few question about comparable methods and equals method. Thank you for answer in so detail. First when you say make the comparable to be generic, that imply I need to make my class declaration like this, right?

public class Distance<T> implements Comparable<T>

For some reasons, it does not like it when I left out 'Distance<T>'
I also have a question about generic class. If I make it generic and I have an inch and feet variable. Shouldn't I make it

Distance<F,T> implements Comparable<F,T>

The last question is about equal method, I don't quite the code you just type up there. I don't understand. Can you explain it a little bit.

You need to add a type parameter to Distance class only if you have generic types as members of the class. A good example for such a class would be "Node<T>" of a linked list which holds a value of type "T" along with a reference to the next node

class Node<T> {
  private T item;
  private Node<T> next;
}

In your case, you need not make your class generic. You can simply do:

class Distance implements Comparable<Distance> {
  public boolean compareTo(Distance that) {
    // code here
  }
}

I also have a question about generic class. If I make it generic and I have an inch and feet variable. Shouldn't I make it

Nope, the type parameter for the Comparable interface is the class which implements Comparable and not the type parameters of your class.

The last question is about equal method, I don't quite the code you just type up there. I don't understand. Can you explain it a little bit.

Which part specifically? I'm just pointing out that you are missing a condition which will help with "fast" execution of equals in case you pass the same object into the equals() call. For e.g. with your current equals() method, if I do:

Distance d = new Distance();
boolean status = d.equals(d);

The equals() call would go ahead and compare the fields when there is no real need to do so given that we know they are the same objects in memory pointed to by different references. Hence adding a condition like `if (this == that) return true` will make sure that no additional computation/checks are done if both the references point to the same object.

Edited 4 Years Ago by ~s.o.s~: n/a

Sorry for did not make my question clear. Now I got the compareTo method finally. You just answer my question about equals method. Thank you so much.

I have another question about custom exception. I know this is out of topic, but I think I just miss a little piece about custom exception. So to create a custom exception. I do the following:

public class CustomException extends Exception 
{
	public CustomException()
	{
		super();
	}
	
	public CustomException(String errMsg)
	{
		super(" "+errMsg);
	}

}

This is my custom exception class. By the way, I want to create an exception for not allow the user set inches and feet to a negative number. After create my custom exception class, I implement it to my distance class like this:

public void setFeet(int ft) 
	{
		if(ft<=0)
			throw new CustomException("This is cannot be zero");
		
			feet = ft;

but I don't get why the compiler is giving me an error. Thank you for being so patient with me.

Sorry for did not make my question clear. Now I got the compareTo method finally. You just answer my question about equals method. Thank you so much.

I have another question about custom exception. I know this is out of topic, but I think I just miss a little piece about custom exception. So to create a custom exception. I do the following:

public class CustomException extends Exception 
{
	public CustomException()
	{
		super();
	}
	
	public CustomException(String errMsg)
	{
		super(" "+errMsg);
	}

}

This is my custom exception class. By the way, I want to create an exception for not allow the user set inches and feet to a negative number. After create my custom exception class, I implement it to my distance class like this:

public void setFeet(int ft) 
	{
		if(ft<=0)
			throw new CustomException("This is cannot be zero");
		
			feet = ft;

but I don't get why the compiler is giving me an error. Thank you for being so patient with me.

check this link here for creating custom exceptions:http://www.seasite.niu.edu/cs580java/testexception.htm

This is my custom exception class. By the way, I want to create an exception for not allow the user set inches and feet to a negative number. After create my custom exception class, I implement it to my distance class like this

There exists one such exception in the Java standard library specifically for this case; it's called IllegalArgumentException which you would normally see when you try to create an ArrayList with negative size etc.

Also, the custom exception which you have created is a "checked" one in the sense that "compiler" checks to ensure that either the caller catches the exception or propagates it to a higher level. Don't use checked exceptions for something which can be easily prevented at runtime. For e.g. in your case you can always check the parameters before passing them to the Distance constructor. Or better yet, just throw a unchecked exception (like IllegalArgumentException mentioned above).

Edited 4 Years Ago by ~s.o.s~: n/a

Sorry for getting back so late. I'm running to another problem now. After I create my custom exception(I don't want to do it, but since my teacher require me do it. I might as well just do it. @s.o.s), I implement it into my set method and I run my test class, it starts to says I fail many tests. Those methods were correct before I implement the custom exception. Where did I do wrong? Thank you so much.

This is my Distance class

public class Distance implements Comparable<Distance>
{
	private int feet;
	private int inches;
	private final int DEFAULT_FT = 1;
	private final int DEFAULT_IN = 1;
	

	//constructors	
	public Distance()
	{
		feet = DEFAULT_FT;
		inches = DEFAULT_IN;
	}

	public Distance( int ft, int in )
	{
		feet = ft;
		inches = in;
	}
	
	//get methods
	public int getFeet()
	{
		return feet;
	}
	
	public int getInch()
	{
		return inches;
	}
	//set methods - validate + whole integers only
	public void setFeet(int ft) //throw CustomException
	{
		try
		{
			if (ft<0)
			{
				throw new CustomException("Distance cannot be negative");
			}
		}
		catch(CustomException c)
		{
			System.err.println(c);
			feet =ft;
		}
		
		
	}
	
	public void setInch(int in)
	{
		try
		{
			if (in<0)
				throw new CustomException("Distance cannot be negative");
			//inches = in;
		}
		catch(CustomException c)
		{
			System.err.println(c);
			inches = in;
		}
		
	}
	//method to add two distance objects
	
	public Distance add(Distance k)
	{
		Distance p = new Distance();
		p.inches = this.inches+ k.inches;
		p.feet = this.feet+ k.feet;
		
		while(p.inches>12)
		{
			p.inches = p.inches-12;
			p.feet++;
		}

		return p;

	}
	//method to subtract two distance objects
	
	public Distance sub(Distance d)
	{
		Distance k = new Distance();
		k.inches = this.inches- d.inches;
		k.feet = this.feet- d.feet;
		while(k.inches<0)
		{
			k.inches = k.inches+12;
			k.feet--;
		}

		return k;	
	}
	//equals() - compares two distance objects for equality
	@Override
	public boolean equals(Object obj)
	{

		if( obj == null)
			return false;
		if(! (obj instanceof Distance) )
			return false;	
			Distance d = (Distance)obj;
		if( this.feet == d.feet && this.inches == d.inches)
			return true;
		else		
		return false;

	}
	//hashCode() - same hash code for equal objects
	@Override
	public int hashCode()
	{
		int prime = 31, result = 1;
		result = prime * result + feet;
		result = prime * result + inches;
		return result;
	}
	//compareTo() - compares two distance objects ( <, >, == )
	@Override
	/*public int compareTo(Object obj)
	{
		//Distance k = new Distance();
		//Distance k1 = new Distance();
		//k1 = (Distance) obj;
		Distance k = (Distance) obj;

		if (this.getFeet() > k.getFeet() )
		{
			return 1;
		}
		else if( this.getFeet() == k.getFeet() )
		{
			if (this.getInch() > k.getInch() )
			{
				return 1;
			}
			else if (this.getInch() == k.getInch() )
			{
				return 0;
			}
			else
			{
				return -1;
			}
			
		}
		else
			return -1;
		
	}*/
	public int compareTo(Distance d)
	{
		//Distance k = new Distance();
		//Distance k1 = new Distance();
		
		if (this.getFeet() > d.getFeet() )
		{
			return 1;
		}
		else if( this.getFeet() == d.getFeet() )
		{
			if (this.getInch() > d.getInch() )
			{
				return 1;
			}
			else if (this.getInch() == d.getInch() )
			{
				return 0;
			}
			else
			{
				return -1;
			}
			
		}
		else
			return -1;
	}

	

	@Override
	public String toString()
	{
		return String.format("%d' %d\"", feet, inches );
	}
	
	
}

This the test class

import junit.framework.TestCase;

public class DistanceTester extends TestCase
{
	private Distance d1;
	private Distance d2;
	
	public DistanceTester()
	{
		
	}
	
	protected void setUp()
	{
		d1 = new Distance( 1, 2);
		d2 = new Distance( 2, 5);
	}
	
	protected void tearDown()
	{
		d1 = null;
		d2 = null;
	}
	
	public void testGetLength()
	{
		assertEquals(1, d1.getFeet() );
		assertEquals(2, d1.getInch() );
	}
	
	public void testSetLength()
	{
		d1.setFeet(3);
		d1.setInch(5);
		assertEquals(3, d1.getFeet() );
		assertEquals(5, d1.getInch() );
	}
	
	public void testAddDistance()
	{
		Distance d3=d1.add(d2);
		assertEquals(3, d3.getFeet() );
		assertEquals(7, d3.getInch() );
	}
	
	public void testAddIncrement()
	{
		d1.setInch(8);
		Distance d3 = d1.add(d2);
		assertEquals(4, d3.getFeet() );
		assertEquals(1, d3.getInch() );
	}
	
	public void testSubtract()
	{
		Distance d3 = d2.sub(d1);
		assertEquals(1, d3.getFeet() );
		assertEquals(3, d3.getInch() );
	}
	
	public void testSubstractIncrement()
	{
		d1.setInch(8);
		Distance d3 = d2.sub(d1);
		assertEquals(0, d3.getFeet());
		assertEquals(9, d3.getInch());
	}
	
	public void testEqual()
	{
		d2.setFeet(1);
		d2.setInch(2);
		assertTrue(d1.equals(d2));
		
	}
	
	public void testEqualFalse()
	{
		assertFalse(d1.equals(d2));
	}
	public void testHashCode()
	{
		d2.setFeet(1);
		d2.setInch(2);
		int i = d1.hashCode();
		int j = d2.hashCode();
		assertEquals("Equal object, should have same hashcode", i, j);
	}
	
	public void testCompareToLess()
	{
		assertEquals(-1 ,d1.compareTo(d2));
	}
	
	public void testCompareToGreater()
	{
		d2.setFeet(1);
		d2.setInch(2);
		assertEquals(0, d1.compareTo(d2));
	}
	
	
	
	
}

This is my custom exception:

public class CustomException extends Exception 
{
	public CustomException()
	{
		super();
	}
	
	public CustomException(String errMsg)
	{
		super(errMsg);
	}

}

Once again thank you so much

This question has already been answered. Start a new discussion instead.