Java超|フィールドとメソッドへのアクセス

変数は戦士本人ではない。参照をたどって、オブジェクトの情報と動きにアクセスしよう。

クラスからオブジェクトを作れるようになると、次に大切になるのが 作ったオブジェクトをどう使うのか です。

Warriorクラスから、悟空、ベジータ、ピッコロのような戦士オブジェクトを作れたとしても、それだけではまだ、その戦士らしさは見えてきません。

名前は何か。
流派は何か。
戦闘力はいくつか。
どんな動きをするのか。

こうした情報をオブジェクトに設定して、はじめて「このオブジェクトは悟空だ」「このオブジェクトは亀仙流の戦士だ」と分かるようになります。

ここで重要になるのが、参照型の変数メンバアクセス です。

Javaでは、クラス型の変数はオブジェクトそのものを直接持っているわけではありません。
オブジェクトがある場所を指し示す役目を持っています。そして、その変数を通して、フィールドやメソッドにアクセスしていきます。

ドラゴンボール風にたとえるなら、変数は悟空本人ではなく、悟空オブジェクトにつながる名札 のようなものです。
その名札をたどることで、悟空の名前、流派、戦闘力、状態表示といった情報や動きに触れられるようになります。

この記事では、参照型の変数とメンバアクセスについて、Warriorクラスから作った戦士オブジェクトを扱うイメージで整理していきます。

変数はオブジェクトそのものではない

クラスからオブジェクトを作るとき、次のような形を書きます。

Warrior goku = new Warrior();

この1行を見ると、つい「gokuという変数の中に、悟空オブジェクトそのものが入っている」と考えたくなります。

しかし、Javaのクラス型の変数は少し違います。

クラス型の変数は、オブジェクトそのものを直接持っているのではなく、そのオブジェクトを参照するための変数 です。

このような変数を、参照型の変数 と呼びます。

つまり、gokuという変数は悟空本人そのものではありません。
正確には、new Warrior()で作られたWarriorオブジェクトを参照するための変数です。

ドラゴンボール風にいうと、gokuは悟空本人ではなく、悟空につながる名札や案内札のようなものです。

Javaの考え方ドラゴンボール風のたとえ
オブジェクト実際に存在する戦士
参照型の変数その戦士につながる名札
参照する名札をたどって戦士に触れる

この考え方は、最初は少し不思議に感じるかもしれません。

でもここを理解すると、フィールドに値を入れる処理や、メソッドを呼び出す処理がかなり分かりやすくなります。

ドラゴンボール風で考える参照型の感覚

戦士たちを管理する未来的な登録ルームに、戦士の名札があるとします。

そこにgokuという名札があります。
でも、その名札そのものが悟空という戦士ではありません。

gokuという名札は、悟空として扱うWarriorオブジェクトはこの先にあります と示している目印です。

つまり、次の関係になります。

役割イメージ
実際の戦士オブジェクト
戦士につながる名札参照型の変数

次のコードを見てみます。

Warrior goku;
goku = new Warrior();

このコードの流れは、次のように考えられます。

コード状態
Warrior goku;Warriorオブジェクトを参照するための変数gokuを用意する
goku = new Warrior();新しいWarriorオブジェクトを作り、gokuがそれを参照する

大事なのは、gokuが戦士本人になったわけではないという点です。

gokuは、作られたWarriorオブジェクトを扱うための参照です。
この参照を使って、その先にあるオブジェクトのフィールドやメソッドにアクセスします。

クラス型の変数は参照型の変数

Javaには、intやdoubleのような基本型と、クラスから作られる参照型があります。

Warriorのようなクラス型の変数は、参照型の変数です。

たとえば、次のコードを見てみます。

Warrior goku;
goku = new Warrior();

このとき、流れは次のようになります。

段階何をしているか
Warrior goku;Warrior型の変数gokuを用意する
new Warrior();新しいWarriorオブジェクトを作る
goku = new Warrior();gokuがそのオブジェクトを参照できるようにする

ここでのポイントは、gokuという変数がオブジェクトを直接中に持っているのではなく、オブジェクトを指し示していることです。

ドラゴンボール風に言えば、gokuは悟空本人ではなく、悟空のいる場所へつながる目印です。

図:参照型の変数はオブジェクトへの名札

この図が示していること

この図は、参照型の変数がオブジェクトそのものではなく、オブジェクトへのつながりを持っていることを示しています。

左側の変数gokuは、実際の戦士そのものではありません。
右側にあるWarriorオブジェクトを指し示す名札です。

矢印は、gokuがそのオブジェクトを参照していることを表しています。

この図から分かることは、goku.nameと書いたとき、gokuという変数の中に直接名前が入っているのではなく、gokuが参照しているオブジェクトのname欄を見に行っているということです。

オブジェクトを作るとフィールドに値を入れられる

クラスを宣言しただけの段階では、フィールドの種類が決まっているだけです。

たとえば、Warriorクラスに次のようなフィールドがあるとします。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;
}

この設計図だけでは、まだ誰のことかは決まっていません。

nameという名前欄はあります。
styleMarkという流派記号欄もあります。
styleNameという流派名欄もあります。
powerという戦闘力欄もあります。

しかし、そこに具体的な値は入っていません。

オブジェクトを作ると、そのオブジェクトが持っているフィールドに実際の値を入れられるようになります。

たとえば、gokuオブジェクトには次のような値を入れられます。

フィールド
name悟空
styleMark
styleName亀仙流
power9000

vegetaオブジェクトには、別の値を入れられます。

フィールド
nameベジータ
styleMark
styleName惑星戦士流
power9500

piccoloオブジェクトには、さらに別の値を入れられます。

フィールド
nameピッコロ
styleMark
styleNameピッコロ魔族流
power8800

つまり、クラスは どんな情報を持てるか を決めます。
オブジェクトは 実際にどんな値を持っているか を表します。

そして、そのオブジェクトのフィールドに触れるために使うのが、メンバアクセスです。

メンバアクセスとは何か

クラスの中にあるフィールドやメソッドは、まとめて メンバ と呼ばれます。

そして、そのメンバを使うことを、メンバにアクセスする といいます。

Javaでは、変数名のあとにドットをつけて、フィールドやメソッドを指定します。

たとえば、gokuが参照しているWarriorオブジェクトのnameフィールドを表したいなら、次のように書きます。

goku.name

styleMarkなら、次のように書きます。

goku.styleMark

styleNameなら、次のように書きます。

goku.styleName

powerなら、次のように書きます。

goku.power

このドットを使った書き方が、メンバアクセスの基本です。

ドラゴンボール風に言えば、gokuという名札をたどって、その先にいる戦士の名前欄、流派欄、戦闘力欄を見に行くような感覚です。

書き方意味
goku.namegokuが参照している戦士オブジェクトの名前
goku.styleMarkgokuが参照している戦士オブジェクトの流派記号
goku.styleNamegokuが参照している戦士オブジェクトの流派名
goku.powergokuが参照している戦士オブジェクトの戦闘力

メンバアクセスでは、ドットの前にある変数が どのオブジェクトに触れるか を示します。
ドットの後ろにある名前が そのオブジェクトのどのメンバに触れるか を示します。

フィールドに値を代入する

実際にフィールドへ値を入れる流れを見てみましょう。

まず、Warriorオブジェクトを作ります。

Warrior goku;
goku = new Warrior();

次に、そのオブジェクトが持っているフィールドへ値を代入します。

goku.name = "悟空";
goku.styleMark = "亀";
goku.styleName = "亀仙流";
goku.power = 9000;

これで、gokuという変数が参照しているWarriorオブジェクトに、具体的な値が入ります。

指定入る値
goku.name悟空
goku.styleMark
goku.styleName亀仙流
goku.power9000

ここでのポイントは、値を入れている相手がgokuという変数そのものではないことです。

値を入れているのは、gokuが参照しているオブジェクトのフィールド です。

gokuという名札をたどった先に、実際のWarriorオブジェクトがあります。
そのオブジェクトのname欄、styleMark欄、styleName欄、power欄に値を入れている、というイメージです。

メンバアクセスは「どのオブジェクトの、どの情報か」を示す

メンバアクセスが大切なのは、どのオブジェクトの、どの情報なのか をはっきり指定できるからです。

たとえば、悟空とベジータの両方を作ったとします。

Warrior goku = new Warrior();
Warrior vegeta = new Warrior();

このあと、それぞれに値を入れるときは次のように書きます。

goku.name = "悟空";
goku.styleMark = "亀";
goku.styleName = "亀仙流";

vegeta.name = "ベジータ";
vegeta.styleMark = "惑";
vegeta.styleName = "惑星戦士流";

このように書くことで、次のように別々の情報を持たせられます。

変数名前流派記号流派名
goku悟空亀仙流
vegetaベジータ惑星戦士流

もしドットを使って区別しなければ、どちらのnameなのか、どちらのstyleNameなのかが分かりません。

メンバアクセスは、複数のオブジェクトを扱うときに、誰の情報なのか を明確にするための書き方でもあります。

参照型の変数とメンバアクセスの関係

ここまでの内容をつなげると、次の流れになります。

段階何をしているか
Warrior goku;オブジェクトを参照するための変数を用意する
goku = new Warrior();新しいオブジェクトを作り、gokuがそれを参照する
goku.name = "悟空";参照しているオブジェクトのnameフィールドに値を入れる
goku.styleMark = "亀";参照しているオブジェクトのstyleMarkフィールドに値を入れる
goku.styleName = "亀仙流";参照しているオブジェクトのstyleNameフィールドに値を入れる
goku.power = 9000;参照しているオブジェクトのpowerフィールドに値を入れる

この流れが理解できると、メンバアクセスは単なるドットの書き方ではなく、参照先のオブジェクトに触れるための方法 だと分かります。

ドラゴンボール風に言えば、次のような流れです。

Javaの流れドラゴンボール風のイメージ
gokuという変数を用意する悟空につながる名札を用意する
new Warrior()で実体を作る戦士の実体を生み出す
gokuが参照する名札が戦士につながる
goku.nameに値を入れるその戦士の名前欄に悟空と登録する
goku.styleNameに値を入れるその戦士の流派欄に亀仙流と登録する

図:メンバアクセスは参照先の情報に触れること

この図が示していること

この図は、複数の参照型変数が、それぞれ別々のオブジェクトを参照している様子を示しています。

gokuは悟空オブジェクトにつながっています。
vegetaはベジータオブジェクトにつながっています。

どちらのオブジェクトにもnameやstyleNameというフィールドがありますが、参照している先が違うため、入っている値も違います。

この図から分かることは、goku.nameとvegeta.nameは同じnameフィールドへのアクセスに見えても、実際には別々のオブジェクトのnameにアクセスしているということです。

プログラム全体で流れを見る

ここまでを、Java風のプログラムで整理してみます。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;
}

class Main {
    public static void main(String[] args) {
        Warrior goku;
        goku = new Warrior();

        goku.name = "悟空";
        goku.styleMark = "亀";
        goku.styleName = "亀仙流";
        goku.power = 9000;

        System.out.println("名前は" + goku.name + "です。");
        System.out.println("流派記号は" + goku.styleMark + "です。");
        System.out.println("流派名は" + goku.styleName + "です。");
        System.out.println("戦闘力は" + goku.power + "です。");
    }
}

このコードの流れを順番に見ていきます。

まず、Warriorクラスがあります。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;
}

このクラスには、name、styleMark、styleName、powerというフィールドがあります。

次にmainメソッドの中で、gokuという変数を用意します。

Warrior goku;

この時点では、まだWarriorオブジェクトは作られていません。
gokuは、Warriorオブジェクトを参照するための変数として用意されただけです。

次に、newでオブジェクトを作ります。

goku = new Warrior();

これで、新しいWarriorオブジェクトが作られ、gokuがそれを参照するようになります。

そのあとで、メンバアクセスを使ってフィールドに値を入れます。

goku.name = "悟空";
goku.styleMark = "亀";
goku.styleName = "亀仙流";
goku.power = 9000;

最後に、設定した値を表示します。

System.out.println("名前は" + goku.name + "です。");
System.out.println("流派記号は" + goku.styleMark + "です。");
System.out.println("流派名は" + goku.styleName + "です。");
System.out.println("戦闘力は" + goku.power + "です。");

このプログラムでは、gokuという参照型の変数を通して、Warriorオブジェクトのフィールドにアクセスしています。

goku.nameという書き方の意味

goku.nameという書き方は、とても重要です。

goku.name

これは、次の2つに分けて考えられます。

部分意味
gokuWarriorオブジェクトを参照している変数
nameそのオブジェクトが持っているフィールド

つまり、goku.nameは、gokuが参照しているオブジェクトのnameフィールド を表しています。

同じように、次のように考えられます。

書き方意味
goku.styleMarkgokuが参照しているオブジェクトのstyleMarkフィールド
goku.styleNamegokuが参照しているオブジェクトのstyleNameフィールド
goku.powergokuが参照しているオブジェクトのpowerフィールド

ドットは、そのオブジェクトの中にあるメンバへ進むための道しるべです。

ドラゴンボール風に言えば、gokuという名札を見つけ、その名札をたどって悟空オブジェクトに近づき、そこにある名前欄や流派欄を見るようなものです。

複数のオブジェクトがあると参照型の意味が見えやすい

参照型の変数の感覚は、複数のオブジェクトがあるときにより分かりやすくなります。

たとえば、悟空とベジータを作るとします。

Warrior goku = new Warrior();
Warrior vegeta = new Warrior();

goku.name = "悟空";
goku.styleMark = "亀";
goku.styleName = "亀仙流";

vegeta.name = "ベジータ";
vegeta.styleMark = "惑";
vegeta.styleName = "惑星戦士流";

このとき、gokuとvegetaは別々のオブジェクトを参照しています。

そのため、同じnameフィールドやstyleNameフィールドを持っていても、中に入る値は別々です。

変数参照しているオブジェクトの名前流派
goku悟空亀仙流
vegetaベジータ惑星戦士流

ここで、変数がオブジェクトそのものではなく、オブジェクトを参照するためのものだと考えると、かなりすっきり理解できます。

gokuは悟空オブジェクトにつながっています。
vegetaはベジータオブジェクトにつながっています。

だから、それぞれ別々の情報を持たせられるのです。

メソッドもメンバアクセスで使う

ここまではフィールドへのアクセスを中心に見てきました。

しかし、メンバアクセスはフィールドだけに使うものではありません。
メソッドもメンバなので、同じようにドットを使って呼び出します。

たとえば、WarriorクラスにshowStatusメソッドがあるとします。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;

    void showStatus() {
        System.out.println("名前は" + name + "です。");
        System.out.println("流派記号は" + styleMark + "です。");
        System.out.println("流派名は" + styleName + "です。");
        System.out.println("戦闘力は" + power + "です。");
    }
}

このとき、gokuが参照しているオブジェクトのshowStatusメソッドを使うなら、次のように書きます。

goku.showStatus();

つまりメンバアクセスは、次のような場面で使います。

使い方
フィールドに値を入れるgoku.name = "悟空";
フィールドの値を取り出すSystem.out.println(goku.name);
メソッドを呼び出すgoku.showStatus();

ドットを使って、参照先のオブジェクトの中にあるメンバへ進む。
この感覚は、フィールドでもメソッドでも同じです。

メソッドを含めたプログラム

showStatusメソッドも含めて、少し実用的な形にすると次のようになります。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;

    void showStatus() {
        System.out.println("名前は" + name + "です。");
        System.out.println("流派記号は" + styleMark + "です。");
        System.out.println("流派名は" + styleName + "です。");
        System.out.println("戦闘力は" + power + "です。");
    }
}

class Main {
    public static void main(String[] args) {
        Warrior goku = new Warrior();

        goku.name = "悟空";
        goku.styleMark = "亀";
        goku.styleName = "亀仙流";
        goku.power = 9000;

        goku.showStatus();
    }
}

このコードでは、まずWarriorオブジェクトを作っています。

Warrior goku = new Warrior();

次に、フィールドへ値を入れています。

goku.name = "悟空";
goku.styleMark = "亀";
goku.styleName = "亀仙流";
goku.power = 9000;

最後に、メソッドを呼び出しています。

goku.showStatus();

showStatusメソッドの中では、name、styleMark、styleName、powerを使って状態を表示しています。

ここで大切なのは、goku.showStatus()と書くことで、gokuが参照しているオブジェクトのshowStatusメソッドを呼び出している という点です。

複数のオブジェクトでメソッドを呼び出す

同じWarriorクラスから複数のオブジェクトを作り、それぞれのshowStatusメソッドを呼び出すこともできます。

class Warrior {
    String name;
    String styleMark;
    String styleName;
    int power;

    void showStatus() {
        System.out.println("名前は" + name + "です。");
        System.out.println("流派記号は" + styleMark + "です。");
        System.out.println("流派名は" + styleName + "です。");
        System.out.println("戦闘力は" + power + "です。");
        System.out.println();
    }
}

class Main {
    public static void main(String[] args) {
        Warrior goku = new Warrior();
        Warrior vegeta = new Warrior();
        Warrior piccolo = new Warrior();

        goku.name = "悟空";
        goku.styleMark = "亀";
        goku.styleName = "亀仙流";
        goku.power = 9000;

        vegeta.name = "ベジータ";
        vegeta.styleMark = "惑";
        vegeta.styleName = "惑星戦士流";
        vegeta.power = 9500;

        piccolo.name = "ピッコロ";
        piccolo.styleMark = "魔";
        piccolo.styleName = "ピッコロ魔族流";
        piccolo.power = 8800;

        goku.showStatus();
        vegeta.showStatus();
        piccolo.showStatus();
    }
}

このコードでは、goku、vegeta、piccoloという3つの変数が、それぞれ別々のWarriorオブジェクトを参照しています。

変数参照しているオブジェクト呼び出しているメソッド
goku悟空オブジェクトgoku.showStatus()
vegetaベジータオブジェクトvegeta.showStatus()
piccoloピッコロオブジェクトpiccolo.showStatus()

同じshowStatusメソッドを呼び出していても、表示される内容はそれぞれ違います。

なぜなら、goku、vegeta、piccoloが参照しているオブジェクトが別々で、それぞれのフィールドに入っている値も違うからです。

図:フィールドもメソッドもドットでアクセスする

この図が示していること

この図は、フィールドもメソッドも、ドットを使ってアクセスすることを示しています。

左側にはフィールドアクセスの例があります。
goku.name、goku.styleMark、goku.styleName、goku.powerのように書くことで、gokuが参照しているオブジェクトの情報に触れています。

右側にはメソッド呼び出しの例があります。
goku.showStatus()、goku.attack()、goku.chargeKi()のように書くことで、gokuが参照しているオブジェクトの動きを呼び出しています。

この図から分かることは、メンバアクセスの基本形は 変数名.メンバ名 であり、ドットの前がアクセス先のオブジェクトを決めているということです。

参照型の変数とメンバアクセスで押さえたい感覚

参照型の変数とメンバアクセスで、まず押さえたい感覚は次の通りです。

ポイント内容
クラス型の変数オブジェクトそのものではなく、オブジェクトへのつながりを持つ
参照変数がオブジェクトを指し示すこと
メンバアクセス変数名.メンバ名で参照先のフィールドやメソッドに触れること
フィールドアクセスオブジェクトの情報を設定したり取り出したりする
メソッド呼び出しオブジェクトの動きを実行する

ドラゴンボール風に言いかえるなら、gokuという変数は悟空本人ではありません。

gokuは、悟空オブジェクトにつながる名札です。
その名札をたどることで、goku.nameやgoku.styleNameにアクセスできます。
goku.showStatus()と書けば、悟空オブジェクトの状態表示の動きを呼び出せます。

クラスからオブジェクトを作るだけで終わりではありません。
そのオブジェクトに情報を入れたり、動きを呼び出したりすることで、はじめてJavaの世界の中で戦士らしく扱えるようになります。