Javaプログラミング入門 17章 [Objectクラス]

Java

こんにちは。ECF Techブログ
担当 Michiharu.Tです。

Javaプログラミング入門記事の第17章をお送りいたします。
第17章のテーマは「Objectクラス」です。

本連載の初回および章立ての一覧については下記のリンクから確認できます。

Javaプログラミング入門 0章
0章 Javaプログラミングを始めよう こんにちは。ECF Techブログ 担当 Michiharu.Tです。 この記事はプログラミング言語のJavaを、実際に動かしながら学びたい人のための学習教材となっています。手を多く動かすこと、感覚を...

本章では、Javaのオブジェクト指向の概念を支える重要なクラスであるObjectクラスについてご紹介します。

Objectクラスはすべてのクラスの親クラスです。ここでいうすべてとは、JDKにあらかじめ含まれるクラスおよびプログラマー自身が作成したクラスのすべてです。

Objectクラス

Objectクラスを活用するメリットには次のようなものがあります。

  • すべてのクラスに共通するメソッドを持たせることができる。
  • すべてのクラスをObject型として扱うことができる。

JDKのクラスライブラリには、このメリットを活かして作成されたものが数多くあります。本章では、この2つのメリットについて具体例を用いて解説し、Objectクラスの活用のしかたを学びます。

17-1 メソッドの活用

最初に「すべてのクラスに共通するメソッドを持たせることができる。」というメリットについて説明します。Objectクラスには様々なメソッドが定義されています。Objectクラスはすべてのクラスの親クラスですので、Objectクラスで定義されているメソッドはすべてのクラスで使うことができます

本節では、よく用いられるメソッドとして次の2つをご紹介します。

メソッド名 概要
toString インスタンスの文字列表現を定義する
equals 2つのインスタンスの同値を定義する

17-1-1 toStringメソッドの動作

始めにtoStringメソッドです。Objectクラスでは次のように定義されています。

public String toString()

toStringメソッドはオブジェクトの文字列表現を定義することを目的としたメソッドです。クラスライブラリ内にあるクラスでも様々なところでtoStringメソッドが呼び出されています。その代表例がSystem.out.printlnです。プログラム例を使って動きを確認してみましょう。

プログラム例(Item.java)

public class Item {
    //フィールド
    String name; //名前
    int price; //価格

    //コンストラクタの定義
    Item(String name, int price){
        //2つのフィールドに引数の値を代入
        this.name = name;
        this.price = price;
    }
}

商品を表すItemクラスを定義しています。2つのフィールドとそのフィールドを初期化するためのコンストラクタ。というシンプルな構成になっています。

次にItemクラスを利用するメインクラスを定義します。

プログラム例(Main.java)

public class Main{
    public static void main(String[] args){
        Item ringo = new Item("りんご",200);
        System.out.println(ringo);
    }
}

Item型のインスタンスを生成し、そのインスタンスをそのままSystem.out.printlnで表示するプログラムです。

では、作成した2つのクラスをコンパイル・実行してみましょう。

実行結果

Item@7a81197d

実行結果としてよくわからない文字列が表示されました。その理由を順に説明します。

実はSystem.out.printlnでは、引数として渡されたインスタンスのtoStringメソッドを呼び出し、その戻り値を表示しています。

ですが、ItemクラスにtoStringメソッドの定義はありません。ではなぜ、toStringメソッドの呼び出しができるかというとObjectクラスに定義されているtoStringメソッドが継承されているためです。

Objectクラスに定義されているtoStringメソッドは、次のような内容を表示することになっています。

クラス名@ハッシュコード

つまり、Objectクラスに定義されたtoStringメソッドがSystem.out.println内で呼び出されることによって、上記のような実行結果が表示されたというわけです。

ハッシュコードはインスタンスを識別するのに用いられる値。と考えておいてください。実行環境によって値が異なります。

17-1-2 toStringメソッドの活用

次にtoStringメソッドを活かして、Itemインスタンスの情報をSystem.out.printlnで表示できるようにしてみましょう。プログラム例を示します。

(Item.java)

public class Item {
    //フィールド
    String name; //名前
    int price; //価格

    //コンストラクタの定義
    Item(String name, int price){
        //2つのフィールドに引数の値を代入
        this.name = name;
        this.price = price;
    }

    //toStringメソッドをオーバーライドする
    public String toString(){
        return "商品名:" + name + " 価格:" + price;
    }
}

toStringメソッドをオーバーライドする記述を追加しました。これによりSystem.out.printlnメソッドは、オーバーライドした方のメソッドを呼び出します(下図)。

toString02

メインクラスを再度実行して確認してみましょう。

実行結果

商品名:りんご 価格:200

System.out.printlnに引数としてItemインスタンスを渡すだけで、情報を表示することができました。

このように、Objectクラスのメソッドはクラスライブラリに含まれる様々なクラスで利用されていますので、自作クラス内でオーバーライドするだけで、様々な恩恵を受けることができます。

17-1-3 equalsメソッドの動作

次にequalsメソッドの動作を見ていきましょう。equalsメソッドは 2つのインスタンスの同値 を定義するメソッドです。同値について、String型のequalsメソッドを例にご紹介します。

プログラム例(Main.java)

public class Main{
    public static void main(String[] args){
        String s1 = new String("hello");
        String s2 = new String("hello");

        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
}

2つの文字列s1とs2を用意し、演算子==を用いた比較とequalsメソッドを用いた比較の両方を行っています。コンパイル・実行すると、次のような結果となります。

実行結果

false
true

==演算子を使った比較において「同じ」とみなされる基準は、「同じインスタンスかどうか」です。プログラム例は3,4行目でそれぞれ文字列「hello」をインスタンスとして生成しています。したがって、変数s1,s2は下図のように、それぞれ異なるインスタンスを指しています。そのため==演算子で判定を行うと「s1とs2は異なる」という判定になります。

一方でStringクラスのequalsメソッドでは「同じ文字列であれば同じとみなす」という定義がなされています。s1とs2は指し示すインスタンスは異なりますが、保持している文字列は同じなのでequalsメソッドの判定ではtrueと表示されています。

equalsメソッド

Javaではこのように「同じ」の判定基準が2つあります。2つの変数が同じインスタンスを指している場合、2つの変数は「等価」である。と言います。一方、2つの変数をequalsメソッドで比較したときにtrueである場合、2つの変数は「同値」である。と言います。

17-1-4 equalsメソッドの活用

次はItemクラスでequalsメソッドをオーバーライドして、Itemインスタンスにおける 「同値」 を定義してみましょう。今回は、2つのインスタンスにおいて名前と価格がどちらも一致していたら「同値」とすることにします。

Itemクラスでequalsメソッドをオーバーライドします。次のようなプログラムとなります。

プログラム例(Item.java)

    :
    public boolean equals(Object obj){
        //引数objがItem型かどうかをチェックする・・(1)
        if(!(obj instanceof Item)){
            return false;
        }
        //Item型に強制変換(キャスト)する・・(2)
        Item item = (Item)obj;

        //名前が異なる場合はfalse
        if( !item.name.equals(this.name) ){
            return false;
        }
        //価格が異なる場合はfalse
        if( item.price != this.price ){
            return false;
        }
        //名前,価格ともに一致していたらtrue
        return true;
    }
    :

コメント(1)について説明します。equalsメソッドの引数はObject型です。そのため名前と価格を比較する前に、そのインスタンスがItem型のインスタンスであるかを調べる必要があります。あるインスタンスが何型か?を調べるために用いられるのがinstanceof演算子です。次のように使用します。

変数名 instanceof 型名

プログラム中のobj instanceof Itemで、引数objがItem型かどうかを調べています。Item型でなければ「同値」とはみなさないのでfalseを返しています。

コメント(2)について説明します。instanceof演算子を使って、objがItem型であることが確認できたら、引数objの指すインスタンスをItem型として扱えるようにします。Item型として扱うためには、型変換を行ったうえでItem型の変数に代入する 必要があります。Item item = (Item)obj;の部分がその記述になります。キャスト演算子を使ってobjをItem型に変換し、変数itemに代入しています。

以降は、変数itemが示すインスタンスと自身のインスタンスの名前と価格が一致しているかどうかをチェックしています。

次にメインクラスを作成して、動作を確認します。プログラムは次のとおりです。

プログラム例(Main.java)

public class Main{
    public static void main(String[] args){
        Item i1 = new Item("りんご",200);
        Item i2 = i1;
        Item i3 = new Item("りんご",150);
        Item i4 = new Item("りんご",200);

        System.out.println(i1.equals(i2));
        System.out.println(i1.equals(i3));
        System.out.println(i1.equals(i4));
    }
}

コンパイル・実行すると、次の結果となります。

true
false
false

mainメソッドで定義されているi1i4の4つの変数は、それぞれ下図のようにインスタンスを指し示しています。

equals02

各System.out.println内のequalsメソッドの動作はそれぞれ次の結果となります。

  • i1とi2を比較、同じインスタンスを指示しているため、名前と価格が一致する。 → true
  • i1とi3を比較、名前は一致するが価格は異なる。 → false
  • i1とi4を比較、インスタンスは異なるが、名前も価格も一致する。 → true

Item型における「同値」を定義することができました。

17-2 Object型の活用

次に2つ目のメリットとして挙げた「すべてのクラスをObject型として扱うことができる。」について説明します。
Objectクラスはすべてのクラスの親クラスですので、たとえばObject型の配列を定義することでどのようなインスタンスでも代入できる配列を作り、まとめて操作することも可能となります。プログラム例を示します。

(Main.java)

import java.util.Date;

public class Main{
    public static void main(String[] args){
        Object[] objs = new Object[3];
        objs[0] = new String("Hello");
        objs[1] = new Item("りんご",200);
        objs[2] = new Date();//(1)

        for(int i = 0; i < objs.length; i++){
            System.out.println(objs[i]);
        }
    }
}

コメント(1)で使われているDateクラスは日付情報を保持するクラスで、クラスライブラリで用意されているものです。また、Itemクラスは17-1-2の例のように、toStringメソッドがオーバーライドされているものとします。

実行結果

Hello
商品名:りんご 価格:200
Wed Jul 17 16:04:17 GMT+09:00 2024

配列objsの各要素をSystem.out.printlnに引数として渡すことで、それぞれの型に適した表示を行っています。最終行の表示は、Dateクラスによる現在日時の表示です。

このようにObjectクラスがあることで、すべてのインスタンスをObject型として扱うことができます。クラスライブラリに含まれるクラスの中には、このことを利用した多態性の恩恵を受けているクラスが多くあります。

タイトルとURLをコピーしました