-2

When I saw this I got baffled:

  public class Timestamp extends java.util.Date {
    //...
    public boolean equals(java.lang.Object ts) {
      if (ts instanceof Timestamp) {
        return this.equals((Timestamp)ts);
      } else {
        return false;
      }
    }
    public int hashCode() {
        return super.hashCode();
    }

It is indeed documented with a bold Note (see https://docs.oracle.com/javase/7/docs/api/java/sql/Timestamp.html)

What could be the cause to make such a, to me, very bad decision? Why not call super.equals(this) when compared to a java.util.Date object to make the equal comparison symmetrical?

Machavity
  • 30,841
  • 27
  • 92
  • 100
estani
  • 24,254
  • 2
  • 93
  • 76
  • Because bad decisions were made back in the days. – Kayaman Jan 03 '18 at 10:54
  • 1
    It is a very old api. It made do with what was available at that time – Thorbjørn Ravn Andersen Jan 03 '18 at 10:55
  • The correct solution would have been if `Date.equals()` would deem a date unequal to an instance of a subclass of `Date`. But when they added the `java.sql` package in JDK 1.1, they didn’t dare make this change in the `Date` class, since this had been around since JDK 1.0. – Ole V.V. Jan 03 '18 at 12:37
  • 1
    The good news is that you need not care. These days you can store `java.time.Instant` and `java.time.LocalDateTime` objects into the timestamp column of your SQL database and retrieve them as the same types again. You don’t need to use the old `java.sql.Timestamp` class at all. – Ole V.V. Jan 03 '18 at 12:39
  • 2
    **Down-Voters: Think twice, and leave a comment along with your vote.** This Question is valid, and shows a thoughtful curious mind. This class inheritance is *indeed a bad design, a flawed hack*, and deserves discussion for inquiring minds. – Basil Bourque Jan 06 '18 at 23:13

3 Answers3

3

Instead, use java.time

As others stated:

  • This class inheritance is poor design, a flawed hack.
  • Now moot, as this class is now legacy, supplanted by java.time.Instant.

Avoid all the old legacy date-time classes found outside the java.time package.

For databases, use a driver compliant with JDBC 4.2 or later to directly exchange java.time objects with your database. You can forget all about java.sql.Timestamp.

Storing:

Instant instant = Instant.now() ;
myPreparedStatement.setObject( … , instant ) ;

Retrieving:

Instant instant = myResultSet.getObject( … , Instant.class ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • I hoped the decision at least made sense at the time Timestamp was implemented. But if everyone agrees this is just a flaw, then so be it. Your answer is the more compelling one. Not sure we can (want to) add this backport you mentioned though, as we are trying to migrate away from this java version. In any case, nice answer. thanks. – estani Jan 08 '18 at 18:01
  • 1
    @estani The back-port project is led by the same man, Stephen Colebourne, as the java.time JSR 310 project. The back-port is designed to be forward-compatible with java.time, using same class and method names. When you move from using the back-port to the built-in classes, you'll need do little more than change your `import` statements. Why bother? Because the legacy date-time classes really are that bad, an awful mess. – Basil Bourque Jan 08 '18 at 19:39
2

What could be the cause to make such a, to me very bad, decision? Why not call super.equals(this) when compared to a java.util.Date object to make the equal comparison symmetrical?

The why is probably only known by the author and not documented. But super.equals(this) will also not respect the contract of equals. As Date misses the nanosecond precision, the only way to do the equals is to leave the nanos out. But this would lead to the situation where two unequal Timestamp instances (with different nano values) would be both be equal to the same Date instance, which would mean that the implementation is not transitive.

DennisV
  • 128
  • 1
  • 10
  • well... not if nano == 0 right? :-) And this is exactly my case... as a work around, I assured the Date object is compared to the Timestamp one. – estani Jan 03 '18 at 16:19
  • If nano == 0, why even bother to use Timestamp? Just use Date and avoid the problem altogether :) – DennisV Jan 04 '18 at 10:12
  • It's a jpa thing. Hibernate (in the version we use) maps Date to Timestamp for postgres. It's not possible to see this from the code, you'll have to debug to see what's really happening. – estani Jan 05 '18 at 12:10
  • Don't know which version you're using but probably it is possible to tell JPA to map it to a Date. either by using a different annotation or by using an attribute type converter. There are many posts on that topic to be found on Stackoverflow. – DennisV Jan 08 '18 at 07:16
2

Date.equals requires Dates, so super.equals is not feasible. Timestamp.equals is overloaded with an equals(Timestamp ts) so also adheres to the equality contract, requiring Timestamps.

The overloading is a bit overdesigned; but I have seen much worse design.

The nanos of Timestamp probably were an addition from the new time API, not available in the old Date.

Considering that the new time API will probably replace this class in the very far future I see no reason for any change request. (The SQL time classes are now a bit redundant.)


Timestamp#equals(Object)
    checks for both to be a Timestamp (as by contract)
    calls Timestamp#equals(Timestamp)

Timestamp#equals(Timestamp)
    calls Date#equals
    and then also compares the extra nanos fields

With "not feasible" I meant that one is not comparing all aspects of both objects.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • I can't understand your answer `Date.equals(Timestamp)` is perfectly well and will compare the objects up to the nanoseconds. Timestamp could have been implemented in case of a non Timestamp object as `/* else */ return other.equals(this);` and it would have been symmetric again. – estani Jan 03 '18 at 16:17
  • Yes I expressed it not well enough: `date.equals(timestamp)` could be called, but `timestamp.equals(date)` not, as it would need to reduce `timestamp` to a date. It is a decision on equality, prefering asymmetry: an air plane and an expensive old timer are not equal when they extend a motorized vehicle with a Rolls-Royce motor instance. – Joop Eggen Jan 03 '18 at 16:40
  • "The nanos of Timestamp probably were an addition from the new time API", that is a wrong statement since this feature (and the whole Timestamp-class) is already since the begin of Java. Many databases have nominal nanosecond precision so nanos were introduced from the start to support this. – Meno Hochschild Jan 05 '18 at 06:03