Ch7. ObjectクラスとDate and Time API

Objectクラス

getClassメソッド

getClass()メソッドに更に、getName()メソッドを繋げることで、インスタンスのクラス名のみを取得することが出来る。

また、ArrayList等ではない普通の配列もオブジェクトとして扱われているので、getClassでクラス名を取得できる。 int等基本データ型には利用できない。

class TestClass{}

public class Chapter7_2 {

    public static void main(String[] args) {
        int[] integerArray = {1,2,3,4,5,6,7};
        String[] StringArray = {"Swinburne", "University", "of", "Techonology"};

        System.out.println(integerArray.getClass()); //'class [I'
        System.out.println(StringArray.getClass());  //'class [Ljava.lang.String;'
        System.out.println(new TestClass().getClass());//'class TestClass'
        System.out.println(new TestClass().getClass().getName());//'TestClass'
    }
}

equalsメソッド

Objectクラスの実装では、同じ参照先を示しているか(同じオブジェクトを指しているか)を示す同一判定である。 サブクラスでオーバーライドされ同値判定に利用されることが多い。例えばStringクラスでは同値判定が実装されているが、StringBuilderでは実装されていない。

なお、hashCodeが同じであること=同一ということではない。 下記の例では、通常のStringオブジェクトと明示的に初期化した(同一内容の)Stringオブジェクトを比較している。

この2つはhashCode()が同じであるにもかかわらず、同値性の確認ではfalseとなっている。 (equals()による比較はStringクラスがこのメソッドをオーバーライドしているため、trueになっている。)

hashCode自体は直接同一性チェックに利用されるものではなく、HashMap等で利用されるものらしい。

class TestClass{}

public class Chapter7_2 {

    public static void main(String[] args) {

        String name  = "All Nippon Airline";
        String name2 = "All Nippon Airline";
        String name3 = new String("All Nippon Airline");

        System.out.println("hashcode : " + name.hashCode() + "\nhashcode2: " + name2.hashCode() + "\nhashcode3: " + name3.hashCode());
        //hashcode : 1111687863
        //hashcode2: 1111687863
        //hashcode3: 1111687863
        System.out.println("name and name2   ==  ?: " + (name == name2));
        //name and name2   ==  ?: true
        System.out.println("name and name3   ==  ?: " + (name == name3));
        //name and name3   ==  ?: false
        System.out.println("name and name2 equals?: " + name.equals(name2));
        //name and name2 equals?: true
    }
}

JavaにおけるequalsとhashCodeを理解する - Qiita

StringBuilderクラスのequlasはオーバーライドされていない。よって同値判定自体が出来ない。 同値判定を行うにはtoString()メソッドを使ってString型の文字列を取得するしかない。

       StringBuilder name4 = new StringBuilder("Aso Taro");
        StringBuilder name5 = new StringBuilder("Aso Taro");

        System.out.println("name4 and name5 equals?: " + name4.equals(name5));
        System.out.println("name4 and name5 equals(toString)?: " + name4.toString().equals(name5.toString()));
        //name4 and name5 equals?: false
        //name4 and name5 equals(toString)?: true

Integerクラスでもequals()メソッドのオーバーライドは行われている。 なお、Integerも-128から127までの間の数のオブジェクトに関しては使いまわしが発生するので、(同値・同一判定を確認するには)明示的に初期化する必要がある。

       Integer number0 = 100;
        Integer number1 = 100;
        Integer number2 = new Integer(100);

        System.out.println("number0 and number1 is same(==)?: " + (number0 == number1));
        System.out.println("number0 and number2 is same(==)?: " + (number0 == number2));
        //number0 and number1 is same(==)?: true
        //number0 and number2 is same(==)?: false

ラッパークラスとプリミティブ型の比較においても、数値オブジェクトの使いまわしによる同一判定が成立してしまう点に注意する。

       int number0Primitive = 100;
        System.out.println("Primitive and Wrapper Class is same(==)?: " + (number0 == number0Primitive)); //'true'

一方で、AutoBoxingを期待した異なるラッパークラス同士の同値判定はfalseになる。同値判定は必ず引数がNullでなく、同じ型のオブジェクトであることが求められる。 因みにAutoBoxing自体が実はintValue()というメソッドを利用して成立している。

Chapter 5. Conversions and Contexts

       Byte numberByte = 100;
        Short numberShort = 100;
        System.out.println("Byte and  Number is equals?: " + (numberShort.equals(numberByte)));//'false'

toString()メソッド

このメソッドも下位クラスでオーバーライドされていることが多いがObjectクラスでは

クラス名 + @ + (オブジェクトの)ハッシュコード

が返却されるよう定義されている。

       TestClass objectTest = new TestClass();
        System.out.println(objectTest.toString());//'TestClass@15db9742'
        TestClass[] objectArray = {new TestClass(), new TestClass()};
        System.out.println(objectArray.toString());//'[LTestClass;@6d06d69c'

2つ目の例では[Lが配列であることを示している。 なお既に述べたのように、toStringのHash値は決してユニークではない。同じ値であっても異なるObjectを指している場合がある。

配列関連のメソッド

arraycopy()メソッド

配列のコピーメソッドはArrayクラスなどではなく、Systemクラスのクラスメソッドとして提供されている。 RubyのようなDeepCopyではない点に注意。また、配列→ArrayListのような異なる型へのコピーは出来ない模様。

       ArrayList<TestClass> arrayListObj = new ArrayList<>(10);
        TestClass[] objectArray2 = new TestClass[3];

        System.arraycopy(objectArray, 0, objectArray2, 0, 2);
        System.out.println("element of copied Arrays is same(==)? :" + (objectArray[1] == objectArray2[1]));//'true'

        System.arraycopy(objectArray, 0, arrayListObj, 0, 2);
        //'Exception in thread "main" java.lang.ArrayStoreException at java.lang.System.arraycopy(Native Method) at Chapter7_2.main(Chapter7_2.java:70)'

Arrays.asList()メソッド

通常の配列をListに変換して返却してくれる。 Listはインターフェースである。しかしasList()で返却されるオブジェクトは配列クラスを参照したArrayListのオブジェクトである。配列を参照しているだけなので、当然addなどは出来ない。 そのため、asList()で返却されるオブジェクトは(addなどのArrayListのメソッドがCallできないように)List型の変数に代入しないとエラーになるような仕組みとなっている。

       List<Object> myList = Arrays.asList(objectArray2);
        //ArrayList<Object> myList2 = Arrays.asList(objectArray2); //'型の不一致: List<TestClass> から ArrayList<Object> には変換できません'
        //myList.add(new TestClass());//'Exception in thread "main"  java.lang.UnsupportedOperationException'
        System.out.println(Arrays.asList(objectArray2).getClass().getName());//'java.util.Arrays$ArrayList'

また、asList()の引数に指定される配列はオブジェクト型が要素になった配列である必要がある点にも注意する。 Listで指定するジェネリクスの型にはプリミティブ型は指定できない。また、asList()では配列の中身のAutoBoxingまでは対応していない。

ただし、asListの引数に直接プリミティブ型の数字を指定する場合は、AutoBoxingが効いて可能になる。

//List<Integer> myList2 = Arrays.asList(primitiveArray);//'型の不一致: List<int[]> から List<Integer> には変換できません'
List<Integer> myList2 = Arrays.asList(10,2,30);//'OK'

Date andTime API

  • LocalDate
  • Localtime
  • LocalDateTime

の3つのクラスが用意されている。

nowofメソッドは全てのクラスメソッドで利用できる。

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Ch7_3 {
    public static void main(String[] args) {
        // TODO 自動生成されたメソッド・スタブ
        LocalTime nowTime = LocalTime.now();
        System.out.println(nowTime); //'12:50:54.356'
        LocalDate nowDate = LocalDate.now();
        System.out.println(nowDate); //'2021-01-10'
        LocalDateTime nowDateTime = LocalDateTime.now();
        System.out.println(nowDateTime);//'2021-01-10T12:53:20.088'

        System.out.println(LocalTime.of(14, 30, 58));//'14:30:58'
        System.out.println(LocalDate.of(1998, 02, 03));//'1998-02-03'
        System.out.println(LocalDateTime.of(1994, 8, 10, 14, 15, 20));//'1994-08-10T14:15:20'

        System.out.println(LocalDate.of(-100, 02, 03));//'-0100-02-03'
        System.out.println(LocalDate.of(1910, 13, 03));//'Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 13'    
    }
}

上記3つのクラスにはparse()というクラスメソッドが文字列→DateTime(またはDate, Time)の変換を行ってくれる。 parseさせる文字列のフォーマットはデフォルトでは下記のような定数によって指定されている。

  • ISO_DATE
  • ISO_TIME
  • ISO_DATETIME
   public void parse() {
        System.out.println(LocalDate.parse("2019-12-13"));
        System.out.println(LocalTime.parse("10:43:12"));
        System.out.println(LocalDateTime.parse("2011-12-03T10:15:30"));
        //標準で採用されているフォーマッタではTZ情報は持てない
        System.out.println(LocalDateTime.parse( "2018-12-03T10:15:30+09:00[Asia/Tokyo]", DateTimeFormatter.ISO_ZONED_DATE_TIME));
        DateTimeFormatter originalFormetter = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH時mm分");
        System.out.println(LocalDateTime.parse( "2020年11月25日15時24分", originalFormetter));
    }

更に、Date and Time APIでは、日時・時刻の計算を行うメソッドを用意している。 これらのメソッドは非破壊的なメソッドである。

       System.out.println(LocalDate.parse("2019-12-13").plusWeeks(200));
        System.out.println(LocalDateTime.parse( "2020年11月25日15時24分", originalFormetter).plusHours(5600));

Duration と Period Class

Durationでは時刻の差を、Periodでは日付の差を扱える。

       Duration a1  = Duration.between(nowTime.plusHours(100), nowTime);
        System.out.println(a1.toDays() + ":" +  a1.toHours() + ":" +  a1.toMinutes());

        Period  a2 = Period.between(LocalDate.of(-100, 02, 03), LocalDate.of(1998, 02, 03));
        System.out.println(a2);