Die äußerst nützliche Java TimeUnit-Enumeration

Obwohl es Teil des Pakets java.util.concurrent ist, ist die TimeUnit-Enumeration in vielen Kontexten außerhalb der Parallelität nützlich. In diesem Beitrag werde ich untersuchen, wie die TimeUnitAufzählung auch in Code verwendet werden kann, der sich nicht direkt mit gleichzeitiger Funktionalität befasst, bevor ich untersuche, wie diese Aufzählung ein Beispiel für viele umfassendere Konzepte in der Java-Entwicklung ist.

Die meisten von uns haben wahrscheinlich Code gesehen (oder implementiert, aber wir werden den anderen Entwickler jetzt dafür verantwortlich machen), wie er in der nächsten Codeliste gezeigt wird. In dieser Codeauflistung wird eine Anzahl von bereitgestellten Millisekunden in eine ganze Anzahl von Tagen umgewandelt, indem durch eine zuvor bestimmte einzelne fest codierte Zahl (86400000, die Anzahl von Millisekunden an einem Tag) dividiert wird.

 /** * Convert provided number of milliseconds into number of days. * * @param numberMilliseconds Number of milliseconds to be converted into days. * @return Number of days corresponding to number of provided milliseconds. */ private static long convertMilliSecondsToDaysViaSingleMagicNumber(final long numberMilliseconds) { // 86400000 = 86400 seconds in a day multipled by 1000 ms per second return numberMilliseconds / 86400000; } 

Es gibt einige Probleme mit dem Ansatz der obigen Codeliste. Das offensichtlichste Problem kann die Verwendung der magischen Zahl 86400000 sein. Obwohl die meisten von uns 86400 als die Anzahl der Sekunden pro Tag erkennen, ist dies möglicherweise nicht für alle offensichtlich, und dann gibt es das Problem, dass es 1000-mal größer als diese Zahl ist . Der Kommentar in der Codeliste hilft, indem er die zugrunde liegende Bedeutung der Zahlen erklärt. Wäre es nicht schön, wenn der Code klarer für sich selbst sprechen würde?

Die nächste Codeliste zeigt eine wohl leichte Verbesserung. Anstatt eine einzelne fest codierte Nummer zu verwenden, werden einzelne fest codierte Nummern verwendet, die besser lesbar sind, weil sie getrennt sind. Ein Leser des Codes hat eine bessere Chance zu sehen, wie die Nummer aufgebaut wurde.

 /** * Convert provided number of milliseconds into number of days. * * @param numberMilliseconds Number of milliseconds to be converted into days. * @return Number of days corresponding to number of provided milliseconds. */ private static long convertMilliSecondsToDaysViaMoreExplanatoryMagicNumbers(final long numberMilliseconds) { // 60 seconds in minute, 60 minutes in hour, 24 hours in day, and // one thousand milliseconds in a second return numberMilliseconds / (60 * 60 * 24 * 1000); } 

Auch wenn die einzelnen Zahlen es möglicherweise einfacher machen, zu sehen, was bei der Konvertierung passiert, kann der Kommentar dennoch hilfreich sein, um sicherzustellen, dass die richtige Funktion gut verstanden wird. Auch magische Zahlen sind weiterhin beteiligt, und die meisten Code-Analyse-Tools melden Probleme bei ihrer Verwendung. Das nächste Codebeispiel versucht, sich mit dem Problem der magischen Zahlen zu befassen.

 private final static int NUMBER_MILLISECONDS_IN_SECOND = 1000; private final static int NUMBER_SECONDS_IN_MINUTE = 60; private final static int NUMBER_MINUTES_IN_HOUR = 60; private final static int NUMBER_SECONDS_IN_HOUR = NUMBER_SECONDS_IN_MINUTE * NUMBER_MINUTES_IN_HOUR; private final static int NUMBER_HOURS_IN_DAY = 24; private final static int NUMBER_MINUTES_IN_DAY = NUMBER_HOURS_IN_DAY * NUMBER_MINUTES_IN_HOUR; private final static int NUMBER_SECONDS_IN_DAY = NUMBER_HOURS_IN_DAY * NUMBER_SECONDS_IN_HOUR; private final static int NUMBER_MILLISECONDS_IN_DAY = NUMBER_SECONDS_IN_DAY * NUMBER_MILLISECONDS_IN_SECOND; /** * Convert provided number of milliseconds into number of days. * * @param numberMilliseconds Number of milliseconds to be converted into days. * @return Number of days corresponding to number of provided milliseconds. */ private static long convertMilliSecondsToDaysViaDefinedConstant(final long numberMilliseconds) { return numberMilliseconds / NUMBER_MILLISECONDS_IN_DAY; } 

Der Ansatz im obigen Code wird häufig im Java-Code verwendet. Die "magischen" Zahlen sind jetzt als Konstanten definiert, die an mehr als nur einer Stelle wiederverwendet werden können. Obwohl dies wohl eine Verbesserung ist, TimeUnitkönnen wir diesen Code weiter verbessern.

 /** * Convert provided number of milliseconds into number of days. * * @param numberMilliseconds Number of milliseconds to be converted into days. * @return Number of days corresponding to number of provided milliseconds. */ private static long convertMillisecondsToDaysViaTimeUnit(final long numberMilliseconds) { return TimeUnit.MILLISECONDS.toDays(numberMilliseconds); } 

Dieser Code nutzt TimeUnitdie MILLISECONDS-Enum-Konstanten- und ToDays-Methode (long), um diese Konvertierung auf einfache Weise durchzuführen. Dies ist eine standardisierte und gut lesbare Methode. Es ist keine magische Zahl in Sicht!

Das obige Beispiel zeigt, wie TimeUnites verwendet werden kann, auch wenn keine Parallelität vorliegt. Außerdem MILLISECONDS, sofern andere Zeiteinheit Darstellungen von TimeUnitumfassen Tagen, Stunden, Mikrosekunden, MINUTEN, Nanosekunden, und SEKUNDEN. Diese decken die am häufigsten verwendeten Zeiteinheiten ab, die man benötigen würde.

Die Methoden in der TimeUnitAufzählung ermöglichen eine einfache Umrechnung von der durch die Aufzählungskonstante dargestellten Einheit in eine andere Zeiteinheit. Es gibt eine allgemeine Konvertierungsmethode TimeUnit.convert (long, TimeUnit), die für diesen Zweck verwendet werden kann. Für die Konvertierung in bestimmte Arten von Zeiteinheiten stehen auch spezifischere Methoden zur Verfügung, sodass der zweite Parameter nicht angewendet werden muss. Diese Methoden umfassen die bereits demonstrierten toDays(long)sowie toHours (lang), toMicros (lang), toMillis (lang), toMinutes (lang), toNanos (lang) und toSeconds (lang). Obwohl die meisten dieser Enum wurde mit J2SE 5 eingeführt, die Methoden toMinutes(long), toHours(long)und toDays(long)wurden mit Java SE 6 eingeführt.

Die TimeUnitbisher definierten Enum-Konstanten und -Methoden sind nicht spezifisch mit Parallelität verbunden und im Allgemeinen nützlich. Die TimeUnitAufzählung bietet drei weitere interessante Methoden. TimeUnit.sleep (long) bietet einen besser lesbaren Thread.sleep (long, int). Die Aufzählungskonstante von TimeUnitimpliziert die anwendbare Zeiteinheit, sodass nur eine Basisnummer angegeben werden muss. Dies impliziert natürlich, dass offensichtlichere Zahlen für den Schlaf bereitgestellt werden können, anstatt sich Gedanken darüber machen zu müssen, ob eine große Zahl in Millisekunden ausgedrückt werden soll oder ob die Methode die Angabe der Zeit in Millisekunden erfordert.

Zwei weitere verwandte nützliche Methoden TimeUnitsind TimeUnit.timedJoin (Thread, long) [Convenience-Methode für Thread.join] und TimeUnit.timedWait (Thread, long) [Convenience-Methode für Object.wait].

Ich habe diesen Beitrag verwendet, um zu demonstrieren, wie TimeUnitoffensichtlich am nützlichsten ist: Er hilft Entwicklern, klaren Code zu schreiben, ohne magische Zahlen für die Konvertierung zwischen verschiedenen Zeitmesseinheiten zu verwenden. Dies ist für sich genommen praktisch, da unterschiedliche APIs häufig unterschiedliche Zeiteinheiten erwarten. Hat TimeUnitjedoch Vorteile, die über die offensichtlichen beabsichtigten Funktionsvorteile hinausgehen. Die TimeUnitAufzählung zeigt die Leistungsfähigkeit von Java-Aufzählungen und wie diese Leistung genutzt werden kann. Ich schaue mir das als nächstes an.

Die meisten von uns, die von C ++ auf Java umgestiegen sind, hatten vor J2SE 5 keine Aufzählung in Java-Versionen. Glücklicherweise hat sich das Warten gelohnt, da die Java-Aufzählung der C ++ - Aufzählung weit überlegen ist. Es gibt zahlreiche Möglichkeiten, wie die Java-Aufzählung besser ist als die C ++ - Aufzählung, aber einer der Hauptvorteile ist die Möglichkeit, Methoden in der Aufzählung zu implementieren. Dies wurde im obigen Beispiel gezeigt, in dem eine toDays(long)Methode die einfache Konvertierung von Millisekunden über den MILLISECONDS.toDays(long)Aufruf ermöglichte. Eine Java-Aufzählung ist viel mehr als nur eine Kapselung einer endlichen Menge integraler Werte. Die Fähigkeit, diesen Aufzählungskonstanten Verhaltensweisen hinzuzufügen, ist sehr mächtig.

Es gibt zwei Hauptansätze zum Definieren von Methoden in einer Aufzählung. Ein Ansatz besteht darin, eine Methode auf der Ebene der gesamten Aufzählung zu definieren und sie auf der Ebene jeder Aufzählungskonstante einzeln zu überschreiben. Der andere Ansatz besteht darin, die Methode einmal für die gesamte Aufzählung und alle ihre Aufzählungskonstanten zu implementieren, ohne dass die einzelne Definition überschrieben werden muss. Mit anderen Worten besteht ein Ansatz darin, eine Implementierung einer Methode für jede Enum-Konstante zu schreiben, und der andere Ansatz besteht darin, eine Methode zu schreiben, die alle Enum-Konstanten gemeinsam haben. Die TimeUnitAufzählung zeigt beide Ansätze. Seine allgemeine convertMethode und all die bequementoXXXXXMethoden (wobei XXXXX Dinge wie Stunden oder Tage sind) werden speziell für jede Enum-Konstante geschrieben, und die übergeordnete Methode auf der gesamten Enum-Ebene löst einen AbstractMethodError aus, wenn sie nicht von jeder Enum-Konstante ordnungsgemäß überschrieben wird (zum Glück ist dies immer der Fall!). Die übrigen öffentliche Methoden ( timedWait, timedJoin, und sleep) mit dem zweiten Ansatz geschrieben: eine einzelne Methode Implementierung für jede dieser besteht , die von jeder ENUM - Konstante verwendet wird definiert TimeUnit.

Neben seiner Nützlichkeit bei der Bereitstellung gut lesbarer Zeiteinheiten-Konvertierungen und neben seiner Nützlichkeit bei der Demonstration der signifikanten Vorteile der Java-Aufzählung TimeUnitbietet es ein Beispiel für ein anderes "häufig wahres" Prinzip in Java: hoch und allgemein nützliche Klassen (oder in diesem Fall Aufzählung) kann oft im SDK gefunden werden, wo Sie es am wenigsten erwarten. Obwohl die Nützlichkeit vonTimeUnitist bei gleichzeitigen Anwendungen offensichtlich, seine Nützlichkeit geht über die gleichzeitige Funktionalität hinaus. Dies ist nicht der einzige Fall, in dem ein allgemein nützlicheres Konstrukt im JDK in einem bestimmten Paket verfügbar ist. Ich habe das oft in Projekten gesehen, an denen ich auch gearbeitet habe. Oft stellt ein Team eine nette Klasse oder Aufzählung für den eigenen Gebrauch zusammen, die allgemeiner anwendbar ist, die jedoch in ihrem eher speziellen Paket verbleibt, anstatt in einem allgemein zugänglichen Paket zu sein.

Wenn wir unsere eigenen Zeitkonvertierungsroutinen erstellen, sehen wir normalerweise fest codierte Zahlen (oder Konstanten, die als definiert sind) mit Werten wie 1000, 60 und 24. Daher ist es nicht überraschend, dass der Quellcode für TimeUnit diese als Konstanten definiert, die es verwendet in seinen eigenen Konvertierungen. Schließlich muss der Gummi auf die Straße kommen und diese Umwandlungen müssen mit diesen harten Zahlen stattfinden. Der Unterschied besteht darin, dass die Verwendung von TimeUnites uns ermöglicht, diese Nummern außerhalb unseres direkten Codes in einer gut getesteten und standardmäßig verfügbaren Aufzählung zu definieren und zu verwenden. Es ist auch interessant festzustellen, dass fest codierte Ganzzahlen in frühen Versionen von verwendet wurden TimeUnit, aber schließlich durch intern definierte Konstanten ersetzt wurden:

// Handy constants for conversion methods static final long C0 = 1L; static final long C1 = C0 * 1000L; static final long C2 = C1 * 1000L; static final long C3 = C2 * 1000L; static final long C4 = C3 * 60L; static final long C5 = C4 * 60L; static final long C6 = C5 * 24L; 

Dieser Beitrag war bereits langwierig, aber ich möchte noch etwas hinzufügen. Dies ist ein einfaches Groovy-Skript, das TimeUnitzeigt, wie viele Stunden, Minuten, Sekunden, Millisekunden, Mikrosekunden und Nanosekunden an einem Tag liegen.

showTimeUnitConversionFactors.groovy

#!/usr/bin/env groovy // showTimeUnitConversionFactors.groovy import java.util.concurrent.TimeUnit println "IN A SINGLE DAY" println "\tHours: ${TimeUnit.DAYS.toHours(1)}" println "\tMinutes: ${TimeUnit.DAYS.toMinutes(1)}" println "\tSeconds: ${TimeUnit.DAYS.toSeconds(1)}" println "\tMilliseconds: ${TimeUnit.DAYS.toMillis(1)}" println "\tMicroseconds: ${TimeUnit.DAYS.toMicros(1)}" println "\tNanoseconds: ${TimeUnit.DAYS.toNanos(1)}" 

Die Ausgabe zum Ausführen dieses Groovy-Skripts wird als Nächstes angezeigt:

Fazit

Die TimeUnitAufzählung ist offensichtlich nützlich für die Konvertierung zwischen Zeiteinheiten in einem gut lesbaren und standardisierten Ansatz. Sein Wert geht jedoch darüber hinaus, da diese SDK-Aufzählung ein Beispiel für die Leistungsfähigkeit der Java-Aufzählung ist und mehrere Möglichkeiten aufzeigt, diese Leistungsfähigkeit zu nutzen.

Zusätzliche Ressourcen

Es gibt einige andere wirklich aufschlussreiche Blog-Beiträge zu TimeUnit. Dazu gehören die Nützlichkeit von java.util.concurrent.TimeUnit, Java TimeUnit: Mehr als nur eine Zeiteinheit, Ermitteln des Unterschieds zwischen zwei Daten in Java und Konvertieren von Zeiteinheiten in Java.

Originalbeitrag verfügbar unter //marxsoftware.blogspot.com/

Diese Geschichte "The Highly Useful Java TimeUnit Enum" wurde ursprünglich von JavaWorld veröffentlicht.