public final class SimpleTime6 { public final int hour, minute; public static final int HOURS_IN_DAY = 24; public static final int MINUTES_IN_HOUR = 60; public static final int MINUTES_IN_DAY = HOURS_IN_DAY*MINUTES_IN_HOUR; public SimpleTime6 (final int h) { this (h, 0); } public SimpleTime6 (final int h, final int m) { assert 0<=h && h<HOURS_IN_DAY; assert 0<=m && m<MINUTES_IN_DAY; hour=h; minute=m; } @java.lang.Override public String toString () { if (hour==0 && minute==0) { return ("midnight"); } else if (hour==12 && minute==0) { return ("noon"); } else { return String.format ("%02d:%02d", hour, minute); } } /* Design for immutabililty. */ public SimpleTime6 add (final int m) { // Wrap around to the next day. final int totalMinutes = Math.floorMod (MINUTES_IN_HOUR*hour + minute + m, MINUTES_IN_DAY); assert 0<=totalMinutes && totalMinutes<MINUTES_IN_DAY; // Integer division truncates return new SimpleTime6 (totalMinutes/MINUTES_IN_HOUR, totalMinutes%MINUTES_IN_HOUR); } public boolean before (final SimpleTime6 t) { return ((this.hour < t.hour) || ((this.hour == t.hour) && (this.minute < t.minute))); } /* An object really ought to have a proper equals method. This requires consistency with hashing. Several subtle issues arise in fullfiling the Java language expectations for these methods. */ @java.lang.Override public boolean equals (final Object other) { if (this == other) return true; if (other == null) return false; if (this.getClass() != other.getClass()) return false; final SimpleTime6 time = (SimpleTime6) other; return this.hour==time.hour && this.minute==time.minute; } // Lazy initialization requires proper synchronization private volatile int hashCode = 0; // unsigned, mod 2^32 number @java.lang.Override public int hashCode () { if (hashCode==0) { int result = 17; result = 37*result + hour; result = 37*result + minute; hashCode = result; } return hashCode; } /* It is a common (and often deserved) complaint that "Java is too verbose" or has too much "ceremony." A significant contributor to this is that while classes can flexibly model a variety of programming paradigms, this invariably comes with modeling overheads -- and in the case of classes that are nothing more than "plain data carriers", these modeling overhead can be out of line with their value. To write a simple data carrier class responsibly, we have to write a lot of low-value, repetitive code: constructors, accessors, equals(), hashCode(), toString(), etc. And developers are sometimes tempted to cut corners such as omitting these important methods, leading to surprising behavior or poor debuggability, or pressing an alternate but not entirely appropriate class into service because it has the "right shape" and they don't want to define yet another class. Brian Goetz https://cr.openjdk.java.net/~briangoetz/amber/datum.html */ }