I encountered a strange problem today: i had a method that took two Date objects as arguments. The calling method passed reference to the very same object as both of them (the method in question was EqualsBuilder.append). The first parameter got passed fine, but the second one not. It was a new Date object that was different from the first one in the sense that all the fields except for year month and day were set to 0. Note that I didn't have any code that would copy the object... Is that a bug in the JVM?
The code was pretty straighforward and I only noticed that because my unit test failed on comparison of what supposed to be the very same object (i initialized it with some random long).
Edit:
- I did not believe that myself...
- I did not assume bug in JVM, I spent literally 4 hours staring at this code and debugging it.
- I looked in the debugger to verify that they are the same object (will also test with == in the calling method on Monday).
- I use 1.6.0_17 on Windows XP
- I can't post the actual code right this second, will do that on Monday.
Edit 2:
- After restarting eclipse, I cannot reproduce the bug
- I have 7 eye witnesses that can testify that it happened :)
- on of those witnesses said that on a previous gig they encountered something to that extent, and that they encountered this bug (or weird behaviour) once in 3 years
- hence, I guess that my odds of reproducing the situation are pretty slim (I really wish I had taken screenshots)
Edit 3:
Here's the code for the class in question:
import java.util.Date; import java.util.List;
import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder;
public class Foo {
private final long roadId;
private final Date creationDate;
private final Date editDate;
private final List<String> vehicleTypes;
private final boolean continuous;
public Foo(final long roadId, final Date creationDate, final Date editDate, final List<String> vehicleTypes, final boolean continuous) {
super();
this.roadId = roadId;
this.creationDate = creationDate;
this.editDate = editDate;
this.vehicleTypes = vehicleTypes;
this.continuous = continuous;
}
public long getRoadId() {
return roadId;
}
public Date getCreationDate() {
return creationDate;
}
public Date getEditDate() {
return editDate;
}
public List<String> getVehicleTypes() {
return vehicleTypes;
}
public boolean isContinuous() {
return this.continuous;
}
@Override
public int hashCode() {
final HashCodeBuilder builder = new HashCodeBuilder();
builder.append(this.roadId);
builder.append(this.creationDate);
builder.append(this.editDate);
builder.append(this.vehicleTypes);
builder.append(this.continuous);
return builder.toHashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Foo)) {
return false;
}
final Foo other = (Foo)obj;
EqualsBuilder builder = new EqualsBuilder();
builder.append(this.roadId, other.roadId);
builder.append(this.creationDate, other.creationDate);
builder.append(this.editDate, other.editDate);
builder.append(this.vehicleTypes, other.vehicleTypes);
builder.append(this.continuous, other.continuous);
return builder.isEquals();
}
}
- And the unit test that failed: import java.util.Arrays; import java.util.Date; import java.util.List;
import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test;
public class FooTest {
private static final boolean CONTINUOUS = true;
private static final Date CREATION_DATE = new Date(12345678901L);
private static final Date EDIT_DATE = new Date(987654321654321L);
private static final long ROAD_ID = 101;
private static final List<String> VEHICLE_TYPES = Arrays.<String> asList("TEST");
private Foo nonEmpty;
private Foo otherNonEmpty;
@Before
public void setUp() {
this.nonEmpty = new Foo(FooTest.ROAD_ID, FooTest.CREATION_DATE,
FooTest.EDIT_DATE, FooTest.VEHICLE_TYPES, true);
this.otherNonEmpty = new Foo(FooTest.ROAD_ID, FooTest.CREATION_DATE,
FooTest.EDIT_DATE, FooTest.VEHICLE_TYPES, FooTest.CONTINUOUS);
}
@Test
public void testEquals() {
assertTrue(this.nonEmpty.equals(this.otherNonEmpty));
}
}
Now, if I changed this:
private static final Date CREATION_DATE = new Date(12345678901L);
private static final Date EDIT_DATE = new Date(987654321654321L);
to this:
private static final Date CREATION_DATE = new Date(109,1,11);
private static final Date EDIT_DATE = new Date(110,3,13);
it worked fine.
I don't think that the code is wrong, especially that after restarting the JVM all the tests passed. At the same time I know that it's highly unlikely that there is a bug in the JVM (although it is a piece of software and the no software is bug-free).
Right now I have checked in the code with the constructor that caused errors in the first place, perhaps I will be lucky enough to encounter that again. Thanks for feedback.