Javaプログラミング基礎

講義資料

オブジェクト指向の概念

Java は「オブジェクト指向 (object oriented) 」のプログラミング言語です。 オブジェクト (object) とは日本語で「モノ」のことです。 オブジェクト指向の概念においては すべてがモノを中心とした考え方をします。 オブジェクト指向概念について詳しく考えてみましょう。

クラスを作成する際に考えること

クラスを作成する際に必要なことは、 「このプログラムにはどのような物や事が関わっているか」 という視点で考えることです。 次に、そのような物や事にどのような「情報」があって、 どのような「機能」や「役割」があるかを考えます。

Javaでは物事に必要な情報のことを「属性」として変数に入れておくようにし、 物事が果たすべき機能や役割を「メソッド」として 処理の手順を書いていきます。

インターネットでのオンラインショッピングやスーパマーケットでの ショッピングカートについて考えてみましょう。

ショッピングカートには「カートの内容」という情報があります。 そして、「カートに商品を加える」、「カートから商品を取り除く」、 「カートの内容をレジで清算する」という機能があるとします。

ショッピングカートのクラスの設計は次のようになります。

class ShoppingCart {
    // カートの内容
    String cartContents;

    // カートに商品 (item) を加える
    void add(String item) {
        ....
    }

    // カートから商品 (item) を取り除く
    void remove(String item) {
        ....
    }

    // カートの内容を清算する
    int checkOut() {
        ....
	return 合計金額;
    }
}

ここでの、カートの中身の商品 cartContents を「属性」と言うのでしたね。 属性の値を変えることでオブジェクトの性質を さまざまに変えることができます。 また、add(), remove(), checkOut() を「メソッド」と言いましたね。 クラスを作る際には、属性とともに、 クラスにどのような機能を持たせるかを考えます。 それをメソッドとして機能の内容を書くのです。

属性の値はメソッドによって読み込んだり、書き換えられたりします。 例えば、 add(item) では、item に指定された商品を 属性である cartContents に加える処理をすることになります。

以上のように、クラスでは属性とメソッドが重要な役割をするのです。

クラスとオブジェクトの違い

上で定義したクラスは、Java がオブジェクト (インスタンス) を作るための設計図として使われます。 設計図からオブジェクトが完成し 実際にオブジェクトが使われるときに、属性に具体的な値が書き込まれます。 この例では、ShoppingCart クラスからオブジェクトを作成して、 そこに商品情報を書き込むことで 具体的なショッピングカートがプログラムの中で使えることになるのです。

オブジェクトの作成と利用

では、実際にショッピングカートを作り利用してみましょう。 自分で定義したクラスは、これまでの「型」と同じように使うことができます。 クラスを用いた変数の宣言とオブジェクトの生成は、 次のように書くことができます。

ShoppingCart myCart = new ShoppingCart();

この文の内容を詳しく見ていきましょう。

  1. 変数の宣言
    ShoppingCart myCart = new ShoppingCart();
    

    これは、int a; と同じように ShoppingCart 「型」の変数 myCart を宣言したということです。 これによって、myCart にショッピングカートの 情報を入れることができるようになりました。

  2. オブジェクトの生成
    ShoppingCart myCart = new ShoppingCart();
    

    クラスから宣言された変数は、そのままでは使うことができません。 new という演算子でオブジェクトを生成する必要があります。 new ShoppingCart() で、実際にデータの出し入れやメソッドの実行が行える オブジェクトが生成されて、データを格納する領域が作られます。

  3. オブジェクトを変数に代入する
    ShoppingCart myCart  =  new ShoppingCart();
    

    イコールを使用して、2 で生成されたオブジェクトを変数 myCart に代入しま す。これで、 myCart がショッピングカートとして使えるようになりました。

オブジェクトにアクセスするためにはドット記号を使う

プログラムの中で、 オブジェクトにアクセスするためにはドット (.) を使います。 どのオブジェクトに対して操作を行うかをドットの左側に書き、 行いたい操作、つまりメソッド名をドットの右側に書きます。

ショッピングカートに対して商品の追加を行いたい場合は、 次のように書くことができます。

今回は、私のショッピングカート (myCart) にリンゴ (apple) を追加したいとします。

myCart.addToCart(apple);

上の例では myCart に商品を加えるために addToCart メソッドの実行を指示しています。 その際に、「どの商品をショッピングカートに追加するか」 という情報が必要になるため、引数 (カッコ内) に商品である apple を指定しています。

これで、めでたく私のショッピングカートにはリンゴが追加されました。

クラスの設計

クラスは属性とメソッドからなるオブジェクトの設計図とみなすことができます。 クラスを設計する際には、正しい属性とメソッドの関係をプログラムとして 書くことが重要です。

属性には、「そのオブジェクトがどのような情報を持っているか」 というデータが収められます。 属性にはそのオブジェクトがどのような状態であるかという情報も 入れることができます。

そして、属性はメソッドの動作に影響を与えることができ、 またメソッドでは属性の値を書き換えることができます。 ここでは、属性とメソッドの関係について詳しく見ていきましょう。

属性の値はメソッドの機能に影響を与える

属性がメソッドの動作に影響を与える例を見てみましょう。

以下のような属性とメソッドを備えた Dog クラスがあるとします。 bark メソッドの動きが年齢によって変わるのが分かるはずです。

class Dog {
    int years;
    String name;

    void bark() {
        if (years >= 10)
            System.out.println("クンクン");
        else if (years >= 5)
            System.out.println("ワンワン");
        else
            System.out.println("キャンキャン");
    }

    ....
    ....
}

class DogTester {
    public static void main(String[] args) {
        .....
        .....
        .....
        //Dogクラスのオブジェクトで10歳のハチ公がいたとして

        hachiko.bark();     //→ 「クンクン」と鳴く

        //Dogクラスのオブジェクトで5歳のポチがいたとして

        pochi.bark();        //→ 「ワンワン」と鳴く

    }
}

属性の値はインスタンスごとに独立である

上の例では name や years という属性を持った 「犬」クラスを定義しました。 ここで次のように2つのオブジェクトを生成したとします。

Dog hachiko = new Dog();
Dog pochi = new Dog();

hachiko の名前や年齢は、 pochi の名前や年齢と異なるのは当り前ですね。 name や years などの属性は オブジェクトごとに独立したものになります。 属性にはオブジェクト固有の状態を示す値を格納することができます。

メソッドに値を渡す

外部からメソッドに値を渡してオブジェクトに仕事をさせることもできます。 例えば、 Dog オブジェクトに対して「3 回鳴け」ということを指示するために、 メソッドに渡す値は以下のように ( ) の中に書きます。

hachiko.bark(3);

メソッドに渡す値のことを引数 (ひきすう) と呼びましたね。

この引数の値がメソッドに伝わると、 メソッドの内部では、変数を通じて引数の値を使うことができます。 次の例は bark メソッドの中身です。

class Dog {
    ....
    void bark(int numberOfBarks) {

        // numberOfBarks 回鳴く処理がここに書かれる

    }
}

hachiko.bark(3) で指定された引数の「3」という値は、 bark メソッドの内部では numberOfBarks という変数を 通じて取り出し使うことができます。

この変数のことを実引数 (じつひきすう) と呼びましたね。 実引数のことを「パラメータ (parameter)」と呼ぶこともあります。

 

 

メソッドには複数の値を渡すことができる

メソッドには複数の実引数を宣言することもできます。 その場合、引数をひとつずつカンマ (,) で区切って書きます。 また、メソッドを呼び出す側の引数の型と順番は、 メソッドの内部で宣言された型と順番にあわせる必要があります。

Dog hachiko = new Dog();
hachiko.bark(3, "ワン");

...

class Dog {
    ....

    void bark(int numberOfBarks, String voice) {

        // numberOfBark 回 「voice」と鳴く処理がここに書かれる

    }
    ....
}

メソッドは値を戻す

メソッドは値を受け取るだけでなく、「値を戻す」こともできます。 ただし、メソッドの戻せる値は 1 つだけです。

メソッドの中身を決めるときには、 そのメソッドがどんな型の値を戻すのかを書く必要があります。

下の例では int 型の値を戻す compute メソッドと check メソッドの利用と、 メソッドの宣言を示しています。

Simple simpleObject = new Simple();
result = simpleObject.compute() - simpleObject.check();
....

class Simple {
    int compute() {
        return 5 * 8;
    }

    int check() {
        return 5 - 2;
    }
}

 

 

return の式の計算結果がメソッド自身の計算結果になる

上のプログラムのこの式の result の値はいくつになりますか。

result = simpleObject.compute() - simpleObject.check();

「5 * 8 - 5 - 2」で 33 というのは間違いです。 上の式の「simpleObject.compute()」の計算結果が 40 となり、 「simpleObject.check()」の計算結果が 3 となり、 結果として「40 - 3」の計算が行われます。答えは 37 です。

効果的な属性の値の変更と取り出し

メソッドの有効な使い方を紹介します。 get メソッドと set メソッドです。 (getter/setter と呼ばれたり、アクセサと呼ばれることもあります。)

get メソッドはオブジェクトの属性の値を取り出すために使うメソッド、 set メソッドはオブジェクトの属性に新たな値を登録するために使うメソッドです。

get メソッドでは戻り値の型を、 set メソッドでは引数の型を、 対象の属性の型と同じにしておきます。

class AnotherPETBottle {
    Juice content;
    int volume;

    Juice getContent() {
        return content;
    }
    void setContent(Juice juice) {
        content = juice;
    }
    int getVolume() {
        return volume;
    }
    void setVolume(int v) {
        if (v >= 0 && v <= 500) {
            volume = v;
        }
        else {
            System.out.println("PETBottle には 0〜500cc の容量しか設定できません");
        }
    }
}

なぜ、属性の値を扱うためにわざわざ get と set の 2 つの メソッドを用意するのでしょうか。

setVolume メソッドを見てください。 このメソッドでは volume に設定できる値の範囲を制限しています。 このように、メソッド経由で値を設定するようにしておけば、 属性に意図しない値が設定されたり、 不正なアクセスが行われることを防ぐことができるのです。

まとめ: オブジェクト指向プログラミングの諸概念

ここまで学んだクラスとオブジェクトについてまとめてみましょう。

「オブジェクト」はオブジェクト指向を理解する上で重要な概念です。

現実世界では、犬、机、パソコン、自転車など身の回りにオブジェクトの例を 見ることができます。 このような現実世界のオブジェクトには、 すべて「状態」と「振る舞い」を持つという特徴を備えています。 例えば、犬には状態 (名前、毛色、種類、空腹) があり、 振る舞い (吠える、歩く、お手をする、ひっかく) があります。

プログラムの世界では、オブジェクトは変数で状態を持ち、 メソッドと呼ばれるオブジェクトに関する処理をひとまとめにした処理単位で 振る舞いを実現します。

プログラムの世界でオブジェクトを用いると、 プログラムの中で扱う様々な対象物をプログラムの中に書くことができます。 住所録の個人データを表すオブジェクトや、 RPGにおける登場人物やアイテムを表すオブジェクトなどです。 また、オブジェクトは画面上のアイコン、ボタンやウィンドウなど GUI (Graphical User Interface) の表示部品を表すためにも使われます。 さらに、抽象的な概念を表すのにも使われます。 マウスのボタンが押されたという行為、 プログラムでエラーが起ったという事態、などです。

どのようなものがオブジェクトになるかを見出し、 その特徴を分析することは重要な問題です。 ここでは、オブジェクトの 2 つの特性について説明します。

1. 属性

オブジェクトは、固有の姿、形、性質などを持っています。 このようなオブジェクトの状態は、属性として表現されます。

属性の値がそのオブジェクトのアイデンティティとなるわけです。 オブジェクトは属性の値によって 他のオブジェクトに対して明確に区別することができます。 オブジェクトは固有の属性値を持つ唯一の存在であると 考えることができます。 このような「固有である」という性質も オブジェクトの重要な特性の一つなのです。

Java では属性を変数として表します。

2. 操作

各オブジェクトは固有の振る舞いを持ちます。 これを操作 (operation) と呼びます。 操作は一般的に動詞で表現されます。

例えば住所録であれば、住所を登録する、住所を検索する、住所を表示する という操作を考えることができます。 また、これらの操作にはパラメータが必要となります。 住所の登録では「登録する住所」です。

Java では操作をメソッドとして表します。 メソッドとはプログラムの一部分をひとまとまりにして、 ある機能を実現するように一つの単位としてまとめたものです。 (メソッドのことをまれにメンバ関数と呼ぶこともありますが、 これは C++ 等の別の言語での呼び方)

メソッドを実行するためには、以下の情報が必要です。

  1. メソッドを実行する対象のオブジェクト名
  2. 実行するメソッドの名前
  3. メソッドに必要なパラメータ

住所録の例で考えてみましょう。

  1. 電大メディ男というオブジェクトに対して、
  2. 住所を登録するように指示する。
  3. その際のパラメータは登録したい住所情報である。

このように、メソッドを実行する際には、 オブジェクトに対してメッセージを送るという考え方をします。

クラスとオブジェクトの違い

クラス

オブジェクトを見いだして、 プログラムの中で「これをオブジェクトとしよう」ということになると、 その対象物はどんなデータ (属性) があり、 どんな操作 (メソッド) ができるのかをプログラムとして記述することになります。 このようなオブジェクトの性質をプログラムとして書いた設計図のことを「クラス」と呼びます。

我々は、実体のモノをある特徴によりグループ化し、 そのグループに対して共通的な名前をつけることで、 モノに対する共通の認識を得ています。 このグループのことが「クラス」です。 クラスは、同じ特性を持つオブジェクトの集合に名前をつけたものです。 オブジェクトの特性を抽象化したものとも言えます。

Javaでは、オブジェクトを利用するためには、 まずプログラムの中でクラスを記述し、 次にクラスに属するオブジェクト (インスタンス) を生成します。 このため、クラスはオブジェクトの設計図である と考えられます。 現実世界において設計図からたくさんの製品が作られるように、 クラスという設計図から、 多くのオブジェクトを生成し利用することが可能になります。

オブジェクト

オブジェクトとは、 クラスから生成されたそのクラスに属するオブジェクトのことです。 このため、オブジェクト指向プログラミングにおいては、 オブジェクトとインスタンスという言葉は同じ意味で使われます。

一般に、クラスを定義しただけでは、そのオブジェクトを利用することはできません。 そのクラスの実体であるオブジェクトを生成して、 はじめて利用可能になるのです。

オブジェクト指向のプログラムの特長

クラスからオブジェクトを生成すれば、 同じ性質のオブジェクトをいくつでも生成でき、 何度でも利用することができるようになります。 また、メソッドの実行によって、 複雑な処理を簡単に何度でも実行できるようになります。

オブジェクト指向のメリットはこれだけではありません。

オブジェクトを使うことによって、 モノを中心にプログラムを書くことができ、 プログラムで扱うデータとそれに対する操作を わかりやすく記述することができます。

一般に、大きなプログラムをひとまとめに書くと理解が難しくなります。 人間はある程度以上に複雑なものを、 すべてを一度に理解することは困難なのです。 このような場合の原則は、全体をいくつかの部分に分けて、 それぞれを別々に理解することです。 オブジェクト指向プログラムはプログラムの中で登場するモノに着目し、 プログラムをオブジェクトの単位で分けることができます。 オブジェクト指向は、人間が現実世界で物事を理解する時の考え方に 近い概念であるため、プログラムの理解が容易になるのです。

オブジェクトは他のプログラムとは独立して仕事をします。 オブジェクトを利用する際は、 オブジェクトが所定の仕事をしてくれると信頼して、 いわばブラックボックスとして考えてしまえば良いのです。

属性を直接操作するのではなく、 必要な操作だけをメソッドとして用意することで、 想定しないような使われ方をして問題が生じることのないように することが可能です。 オブジェクトは、 データとその操作が一体化されたものなのです。 このような概念をカプセル化 (encapslation) と言います。

また、処理を行う際にメソッドを用いることで、 オブジェクトの中にどういう変数があり、 どのような計算が行われているかといった、 オブジェクトの内部構造や処理手順を把握する必要はありません。 知っていれば良いのはメソッドの使い方だけです。 このような概念を情報隠蔽と言います。