0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaのCalendarクラスで、フィールド値を取得する際に、どうしてgetメソッドを使用する必要があるのか?

Last updated at Posted at 2021-04-02

#概要
Calendarクラスにて、フィールド値を取得する際に、getメソッドを使用してフィールド値を取得する必要がある。
ただ、それが何故かわからなかったので、調べた結果をここに記載する。
(少し、冗長になってしまったので、記事全体が分かりにくくなっています。
ただ、記事全文を見なくても、最低限、仮説と結論だけを見れば、意図するところは伝わるように作っています。)

##疑問の経緯
当初の私は、定数SECONDは、public final static int で宣言されているフィールド値であるので、
getメソッドで、わざわざ、SECONDを引数に指定しなくても、
直接、calendar.SECONDで指定すれば、秒数が取得できるのではないかと考えたのだ。

#私の想定したコード

正常に動かないコード
public class Main {
  public static void main(String[] args){
    //  現在時刻を取得
    Date now = new Date();
    //  カレンダークラスのインスタンスを取得(カレンダークラスが抽象クラスのため、newできない)
    Calendar calendar = Calendar.getInstance();
    //  カレンダークラスの各フィールドにDateクラスのインスタンスの情報を渡してあげる。
    //  今ここで、カレンダークラスは、現在時刻が保持されている
    calendar.setTime(now);
    //  現在の時刻の秒数を表示する。(※書き換えた!)
    //  System.out.println(calendar.get(calendar.SECOND));
    System.out.println(calendar.SECOND);
  }
}
実行結果(異常)
ユーザー名@コンピュータ名 14章 % java Main      
13    // 値が更新されていない!!
ユーザー名@コンピュータ名 14章 % java Main
13    // 値が更新されていない!!
ユーザー名@コンピュータ名 14章 % java Main
13    // 値が更新されていない!

#正常な動作をするコード

正しい動きをするコード
public class Main {
  public static void main(String[] args){
    //  現在時刻を取得
    Date now = new Date();
    //  カレンダークラスのインスタンスを取得(カレンダークラスが抽象クラスのため、newできない)
    Calendar calendar = Calendar.getInstance();
    //  カレンダークラスの各フィールドにDateクラスのインスタンスの情報を渡してあげる。
    //  今ここで、カレンダークラスは、現在時刻が保持されている
    calendar.setTime(now);    // ①
    //  現在の時刻の秒数を表示する。
    System.out.println(calendar.get(calendar.SECOND));    // ②
  }
}
実行結果(正常)
ユーザー名@コンピュータ名 14章 % java Main      
25(秒)    // 現在時刻の秒数が正常に出力されている。
ユーザー名@コンピュータ名 14章 % java Main
26(秒)    // 現在時刻の秒数が正常に出力されている。
ユーザー名@コンピュータ名 14章 % java Main
27(秒)    // 現在時刻の秒数が正常に出力されている。

#考察① 変数SECONDが示すものとは?
##発生事項
さらに、先ほどの実行結果(異常)の部分の結果は、
いつどのタイミングで、実行しても、必ず「13」
さらに、いつどのタイミングで、コンパイルして、実行したとしても、必ず「13」だった。

##仮説
上記の、「13」という数値は、そもそも秒数を表しているものではないのではないか?

##検証
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.javaの
538行目:で、
public final static int SECOND = 13;
という風に、フィールド変数に固定値が直接、代入されていた。

##結論
SECOND変数は、直接的に、秒数を表現するものではないということがわかる。

#考察② 変数SECONDを用いて、どのように秒数を表現するのか?
変数SECONDで、Calendar.java内を検索したものの、70件もヒットしたので、パス。

###仮説① calendar.setTime(now);で、恐らく秒数が処理されているのでは?
###検証
正しい動きをするコードの①部分、
calendar.setTime(now);で、恐らく秒数が処理されていると仮定して、
setTimeメソッドの中をみてみる。
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.javaの
1769行目:で、
setTimeメソッドが定義されており、
その中では、
setTimeInMillis(date.getTime());が記載されている。

では、setTimeInMills(引数LONG値)メソッドの定義は?
引数にLONG値を取る。
メソッドの内容は私の実力では、読み取ることができなかったが、
// If we don't need to recalculate the calendar field values,
// do nothing.
とコメントアウトされていたので、
恐らく、引数のLONG値に変換されたDate型の時間情報を更新する必要があれば、
更新するというメソッドであると推察する。

###結論(仮説①に対する)
calendar.setTime(now)では、時間情報をDate型変数から受け取り、
必要とあれば、その際に時間情報を更新するだけでメソッドであり、
仮定①で仮定したような、秒数が直接的に計算されているわけではないと推察した。

###仮説② calendar.get(calendar.SECOND);で、恐らく秒数が処理されているのでは?
###検証
正しい動きをするコードの②部分、
System.out.println(calendar.get(calendar.SECOND));
のgetメソッド内で、やっているのではないか?
getメソッド内をみる。
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.javaの
1824行目:で、
public int get(int field)と宣言されていて、
内部で、 complete()【A】を呼び出し、戻り値で、internalGet(field)【B】を返している。

####【A】complete()メソッドは何か?
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.javaの
2262行目:で、
変数isTimeSetがFALSEだったら、updateTimeメソッドを呼び出す。(変数isTimeSetは、『timeの値が有効である場合はtrueです。field[]の項目を変更することで、timeは無効になります。』(※javadoc参照)と記載がある。)
※正直ソースコードは理解できなかった。

その後、areFieldsSetがFALSEまたは、areAllFieldsSetがFALSEだったら、computeFieldsメソッドを実行し、
areAllFieldsSet = areFieldsSet = true;にする。
(変数areAllFieldsSet…『fields[]が現在設定されている時間と同期をとっている場合はtrueです。』(※Javadoc記載)
変数areFieldsSet…『fields[]が現在設定されている時間と同期をとっている場合はtrueです。falseの場合は、次にフィールドの値を取得しようとしたときに、timeの現在値からすべてのフィールドを再計算するよう強制します。』(Javadoc参照))
(computeFieldsメソッド…『現在のミリ秒単位の時間値timeをfields[]内のカレンダ・フィールド値に変換します。これによって、カレンダ・フィールドの値を、カレンダに設定されている新しい時間と同期させることができます。』(※Javadoc参照))

####結論:Completeメソッド処理内容
時間情報を更新して、各フィールドと結びつく値
(この場合、定数SECOND[=13]に結びつく値(つまり秒数))を更新するのだという結論を出した。
(※この結論は間違っている可能性があります。というのも、私自身が、ソースコードを理解できなかったからです。)

疑問点:
また、calendarクラスで定義されているcomputeFieldsメソッドは、
抽象メソッドであり、calendarクラスのcomputeFieldsメソッドに
処理内容が全く記載されていなかったので、
ソースコードから、このメソッドが何をやっているのかがわからなかったです。

恐らく、どこかで、このメソッドが実装されているはずなので、
それをご存知の方は、ご教授下さると幸いです。

####【B】戻り値internalGet(field)【B】の部分
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.javaの1838行目:で、
fields[field]というふうに、フィールド値(field:(この場合SECONDS)とその値fields[field](秒数)を取り出している。

##結論:仮説②は正しい!
calendar.get(calendar.SECOND);
上記のように指定した場合、
getメソッドの中で、同期が必要であれば、時間を同期し、フィールド値(calendar.SECOND)に対応する値(秒数)を取得する。

#最終結論
フィールド値(今回の場合Calendar.SECOND)はただの定数値。
(Calendar.SECONDの場合、私の環境だと定数「13」)
フィールド値(今回の場合SECOND)と結びつく値(秒数)を取得したい場合は、
getメソッドで、引数にフィールド値(今回の場合SECOND)を指定して、
実際の値(今回の場合は秒数)を取得する必要がある。

#参考文献
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/src/java/util/Calendar.java
https://sun.ac.jp/prof/yamagu/java8docs/api/java/util/Calendar.html

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?