Showing posts with label value classes. Show all posts
Showing posts with label value classes. Show all posts

Wednesday, 22 June 2011

Avoid subtraction based comparison between integers

Before I say anything I want to share with you a code snippet that is a simplified version of something I have recently wrote in my work. There is a small, yet painful bug in this code… can you see it?

public static class Point implements Comparable<Point> {
private int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public int compareTo(Point other) {
if (this.x != other.x) {
return this.x - other.x;
} else {
return this.y - other.y;
}
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Point)) return false;
Point other = (Point) obj;
return (this.x == other.x && this.y == other.y);
}

@Override
public int hashCode() {
return 31 * x + y;
}
}

This is a simple value class represanting a point. At first glance everything is good with it: the hash function is not sophisticated, but it is correct and legal. The equals() method is written ‘by the book’ so it is probably OK. The class implements Comparable interface and defines a lexicographic order of the points – the code of compareTo() is standard and fulfills the contract of the interface. All three methods are compatible with each other: hash codes of equal points are equal, comparisons are transitive, if A.compareTo(B) == 0 then A.equals(B)… Basically all is fine, so where is the bug? Is there any bug at all?
Unfortunatelly there is a bug in this code. What is worse you probably read about it many times (’Effective Java’ item 11, ‘Java Puzzlers’ puzzle 65, the list goes on an on…). The problem with this code is that substraction based comparators are BAD as they often exposes you to danger of an overflow. This is what happens in this case – see the following:

public static void main(String[] args) {
Point pZero = new Point(0, 0);
Point pPlus = new Point(2000000000, 0);
Point pMinus = new Point(-2000000000, 0);

System.out.println( pPlus.compareTo(pZero) > 0 );
System.out.println( pZero.compareTo(pMinus) > 0 );
System.out.println( pPlus.compareTo(pMinus) > 0 );
}

We are comparing in this code three points. When you look at the declaration of those object you can already see the order of them: pPlus > pZero > pMinus. Just to be sure we check it by printing to console the result of compareTo() function. If you execute this code you’ll get that as expected ‘true’ for pPlus > pZero and ‘true’ for pZero > pMinus. The suprizing thing is the last comparison that evaluates to false. This is because of the integer overflow: in pPlus.compareTo(pMinus) the result value is 2000000000 – (-2000000000) == -294967296! Scary, right?

Saturday, 30 April 2011

== vs equals method in case of String and primitive types

Object equality is tested using == operator. Here it is checked whether 2 references are pointing to same object or not.
While value equality is tested using the .equals(Object) method.
So consider following code snippet:
String one = new String("abc");
String two = new String("abc");


So here

one==two is false

one.equals(two) is true

one==two, because we are creating 2 new objects as specified by new keywords. But as their values are same one.equals(two) returns true because values are same.

Now if we say:

String three = one;



Now both one==two as well one.equals(two) returns true.

Using new keyword vs not using it to create 2 strings


Now consider 2 cases, when we create 2 strings with same value, but first using new keyword and then not using new keyword:

Case1 : Using new keyword:

String data = new String("123");
String moreData = new String("123");



Here data==moreData is false.

Case 2 : Not using new keyword:

String letters = "abc";
String moreLetters = "abc";


Here data==moreData is true.

Why?

Explaining the case 1 is very simple. Because it falls inline with our earlier explanation of == and != operators. But how can we explain case 2, where string comparison using == operator, returns true.

This is due to the compiler and runtime efficiency. In the compiled class file only one set of data "abc" is stored, not two. In this situation only one object is created, therefore the equality is true between these object. Read here for more on this. Not only strings , compiler also support this type of constant pooling for integers, bytes and other value types. See here for this.


Note:

Even though one set of data "123" is stored in the class, this is still treated differently at runtime. An explicit instantiation is used to create the String objects. Therefore, in this case, two objects have been created, so the equality is false. It is important to note that "==" is always used for object equality and does not ever refer to the values in an object. Always use .equals when checking looking for a "meaningful" comparison.

Value objects or VOs in java

//to be written soon

equals() for String, StringBuilder and StringBuffer

Rule of equality in java:

Equals method should be used for checking the logical equality of objects of a class but it does not mean that every class is a candidate for overridden equals method

So if you go and see the source code of StringBuffer/StringBuilder classes in Java, you will find that equals() method is not overridden but it is overridden in the String class.

public class Test{

public static void main (String args[]) {
String str1 = "abcd";
String str2 = "abcd";
System.out.println(str1.equals(str2));

StringBuffer sb1 = new StringBuffer(str1);
StringBuffer sb2 = new StringBuffer(str1);
System.out.println(sb1.equals(sb2));
}
}

Output:

true
false


For String objects, the logical equality would mean the contents of the string should be same.

For StringBuffer and StringBuilder we have following facts:
  • Logical equality means that both objects were configured and assembled using the same String instance and not whether the contents of those String objects is same.
  • The intended use of StringBuffer and StringBuilder is for maintaining a buffer of characters which may change with time.
Both the above statements contradict with each other because the String object being used for building a StringBuffer/StringBuilder object can change very frequently and it does not make any sense to compare the StringBuffer/StringBuilder objects.

This is the reason that the equals() method is not overridden in StringBuffer/StringBuilder classes.

It should be noted that there a misconception that is going around the developer community regarding the above reason which says that then why an ArrayList/Date overrides the equals() method as it too can change with time. Here are a few reasons to clear that thought which in fact is natural:
  • StringBuffer/StringBuilder classes are intended to be used as temporary buffer and change more frequently than classes like ArrayList and Date
  • While one may like to perform various operations like serialization, making clones, being used as keys in collections or stored in database but one is very unlikely to perform similar operations with StringBuffer/StringBuilder classes.
Also String may be treated as value object, where value equality means, object equality. Read here for more on value objects.


Thursday, 24 February 2011

Type of classes

There can be many classification of classes:
1. Final
Base Class,
Derived Class
Value classes