一番かんたんなJava入門

これからJavaを始めようという人の為の超入門サイトです。丁寧、簡単にこだわった解説なので初心者にぴったりです。

【Java】 アクセス修飾子って何? ゲッターって何?

time 2013/07/10

sponsored link

他のクラスからのアクセス

 今回はアクセス修飾子というのが何なのかを分かりやすく説明したいと思います。オブジェクト指向の三大要素の一つである「カプセル化」というのを実現する為に、アクセス修飾子は非常に重要な役割を果たします。
 アクセス修飾子と言えば、「public」というのがそれです。クラスの前に、

public class クラス名

という風につけたり、mainメソッドにも

public static void main(String[] args)

絶対つけていますよね。
「public」の他に「private」と「protected」と、もう一つ、デフォルトってのがあります。デフォルトっていうのは何かと言うとアクセス修飾子を何もつけていない状態です。何もつけていなかったらアクセス制限について何も規定していないのではなく、何もつけていないなりのアクセス制限がかけられることになります。
 表にするとこんな感じです。

public どこからでもアクセス可能
protected 同一パッケージ内のクラス、継承したクラスからのみ可能
デフォルト 同一パッケージ内のクラスからのみ可能
private そのクラスからのみ可能

 下に行くほどアクセス制限が強いわけですが、とりあえず初めはpublicとprivateだけ使えるようになったらいいと思います。つまり、どこからでもアクセスできるのか、それとも、そのクラスからしかアクセスできないのか。
 このアクセス制限というのが具体的にどういうことなのかを分かりやすく説明するために、以下のクラスを使います。

Human.java

public class Human{
        static int count_Human = 0;        // メンバ1
	String name;                       // メンバ2
        int birthday;                      // メンバ3
        int manpukudo;                     // メンバ4

        Human(String name, int birthday, int manpukudo){  // コンストラクタ1
                this.name = name;
                this.birthday = birthday;
                this.manpukudo = manpukudo;
                count_Human++;
        }
        Human(String name, int birthday){               // コンストラクタ2
                this(name, birthday, 50);
        }
        Human(){                                          // コンストラクタ3
                this("不明", 0, 50);
        }

        void eat(){                   // メンバ5
                this.manpukudo += 60;
        }
}

 続けて読んでいただいている方は、見慣れたかもしれませんが、これは人間オブジェクトを作るためのHumanクラスです。Humanクラスにはご覧のように5つのメンバと3つのコンストラクタがあります。
 まず注目してもらいたいのが、staticなフィールドであるcount_Humanです。count_HumanはこのHumanクラスのオブジェクトを作られた数をカウントしているstaticな変数です。newされる度にカウントが一つ増えるようにコンストラクタ1の中に「count_Human++;」を仕込んでいます。(参考:オブジェクトって何? 〔コンストラクタとは〕 〔this( )の意味〕

Human(String name, int birthday, int manpukudo){  // コンストラクタ1
                this.name = name;
                this.birthday = birthday;
                this.manpukudo = manpukudo;
                count_Human++;
        }

 以下はHumanクラスと同パッケージ内にあるHumanTestクラスです。HumanTestクラスでHumanオブジェクトを作って、上記のcount_Humanの値がちゃんと変わっていくのを確認できます。

HumanTest.java

public class HumanTest{
	public static void main(String[] args){
                System.out.println(Human.count_Human);  //①
		
                Human human1 = new Human();
                System.out.println(Human.count_Human);  //②
                
                Human human2 = new Human();
                System.out.println(Human.count_Human);  //③
	}
}

 ①の段階ではまだ一つもHumanオブジェクトは作られていないので「count_Human」は0です。②の段階では1、③の段階では2になります。
 さて、「count_Human」は確かにHumanオブジェクトが作られる度に1増えるので、その値はHumanオブジェクトの数と等しくなります。が、やろうと思えばこんなこともできてしまいます。

HumanTest.java

public class HumanTest{
	public static void main(String[] args){
                System.out.println(Human.count_Human);  //①
		
                Human.count_Human = 100;

                Human human1 = new Human();
                System.out.println(Human.count_Human);  //②
                
                Human human2 = new Human();
                System.out.println(Human.count_Human);  //③
	}
}

 見ての通り、count_Humanに100を代入しています。これでは②の時点でcount_Humanは101になってしまいます。こんなことをされたらHumanオブジェクトの数を表すはずの変数count_Humanの値の整合性が合わなくなります。count_Humanの値はHumanクラスのコンストラクタが呼ばれた時に1増えるだけでいいんです。整合性を保とうと思えば、他のクラス(HumanTestクラス)から上記のような方法で勝手に値を代入されては困ります。それを防ぐ為にアクセス修飾子を使って変数count_Humanにアクセス制限をかけます。

privateなメンバ

 現状でcount_Humanのアクセス制限はどうなっているかと言うと、

Human.java

public class Human{
        static int count_Human = 0;
        String name;
        int birthday;
        int manpukudo;

           //以下省略

}

 変数count_Humanの宣言部分に「public」も何もついていませんね。ということは、count_Humanのアクセス制限はデフォルト状態です。デフォルトでは同一パッケージ内であれば他のクラスからのアクセスは可能な状態です。HumanTestクラスはHumanクラスと同じパッケージ内にあるので、HumanTestクラスの中から「Human.cout_Human」とやれば普通にHumanクラスのcount_Humanの値にアクセスし書き換えることができます。現に先ほどやりました。そんな他のクラスから無理矢理、手を突っ込んでこの変数の値をいじられるような事態を防ぐために、変数count_Humanにアクセス制限をかけます。

Human.java

public class Human{
        private static int count_Human = 0;
        String name;
        int birthday;
        int manpukudo;

           //以下省略

}

 変数count_Humanの宣言部分に「private」というアクセス修飾子をつけました。「private」をつけると、その変数は他のクラスからアクセスできなくなります。つまり、

Human.count_Human

とできなくなります。privateな変数にこれをやるとコンパイルが通りませんので鉄壁のガードです。これで他のクラスから、

Human.count_Human = 100;

こんな横暴はできなくなるので、Humanオブジェクトの数を表すcount_Humanの整合性が崩れることはありません。count_Humanの値が書き換えられるのはHumanオブジェクトを作った時だけということになります。ちなみにprivateなメンバにもそのクラス内からなら自由にアクセスできるので、Humanクラスのコンストラクタ内で

count_Human++;

とやるのは全然問題ありません。そのクラス内でいじっているのか、他のクラスから手を突っ込んでいじっているのかの違いをよく理解する必要があります。
 繰り返しになりますが、他のクラスから

Human.count_Human

とやって変数count_Humanにアクセスするのを防ぐのがアクセス修飾子privateの役割です。

privateなメンバにアクセスするゲッター

 これで、count_Humanはprivateなメンバになったので、HumanTestクラスからアクセスできなくなりました。これで整合性は保てるようになったのですが、一つ問題が発生します。

HumanTest.java

public class HumanTest{
	public static void main(String[] args){
                System.out.println(Human.count_Human);

                Human human1 = new Human();
                System.out.println(Human.count_Human);
                
                Human human2 = new Human();
                System.out.println(Human.count_Human);
	}
}

 これは先ほどもやりましたが、count_Humanの値の変移が分かるようにインスタンスを作ってはcount_Humanの値を出力するというやつです。ここでも「Human.count_Human」というやり方でHumanクラスのcount_Humanにアクセスしています。しかし、count_Humanがprivateなメンバになったことで、これもできなくなってしまうんです(コンパイルエラーになる)。この操作は何もcount_Humanの整合性を崩すような悪さをしているわけではありませんが、privateなメンバには、悪さをしようとしなかろうと、上記の方法ではアクセスできません。
 困ったことになりました。Humanオブジェクトの数を正確にカウントできるようにcount_Humanをprivateにしたのに、その為にcount_Humanの値を取り出せないなんて本末転倒とはまさにこのことです。そこで登場するのがゲッターと呼ばれるメソッドです!
 privateなフィールドの値をゲットするメソッド、それがゲッターです。Humanクラスにcount_Humanの値をゲットするゲッターメソッドを作ってあげましょう。

Human.java

public class Human{
        private static int count_Human = 0;
	String name;
        int birthday;
        int manpukudo;

        // ~コンストラクタ省略~

        public int getCount_Human(){
                return count_Human;
        }

        void eat(){
                this.manpukudo += 60;
        }
}

 これがゲッターです。ゲッターというのはただのメソッドであって、以下の特徴を持つメソッドをゲッターと呼称しているにすぎず、特殊なメソッドでもなんでもありません。普通のメソッドです(ちなみにコンストラクタというのはメソッドではないので、同じようなニュアンスで捉えないように)。
 ゲッターにはpublicをつけます。publicというのはどこからでもどうぞご自由にアクセスして下さいという意味のアクセス修飾子です。ゲッターは原則としてアクセス制限をつけずに公開します。
 ゲッターはゲットしたいフィールドの値を戻り値として返します。なのでその戻り値の型をメソッド名の前に書きます。ここではゲットしたいフィールドはint型のcount_Humanなので、intと書いています。
 ゲッターの名前は普通は「getフィールド名」という形にします。フィールド名の頭文字を大文字にするのが普通です。ここでは「getCount_Human( )」としています。ただし、これはただのメソッド名なので絶対そうしなければならないということではありません。
 ゲッターメソッドの中身は、「return ゲットしたいフィールド名;」だけです。ただ単にそのフィールドの値を返すだけのメソッドにします。ここでは、「return count_Human;」としています。
 これでゲッターの完成です。
 と言いたいところなんですが、ちょっと話が混乱するかも知れませんが、おさらいも兼ねてもう一歩。。count_Humanはstaticなフィールドです。staticなフィールドということはインスタンスにではなくクラスに属するフィールドだと言うことですね。ところがこのゲッターは非staticなメソッドです。つまりインスタンスメソッドです。インスタンスメソッドということは、他のクラスからこのゲッターメソッドを呼ぶ時に、

Human human1 = new Human();
human1.getCount_Human()

という風にインスタンスを作ってから、インスタンスメソッドとして呼ばなければなりません。クラスフィールド(staticなフィールド)はせっかくインスタンスを作っていなくても存在しているフィールドなのに、それをゲットするゲッターがインスタンスメソッドだと、インスタンスがなければそのクラスフィールドにアクセスできないことになります。伝わりますでしょうか?(ゲッターの説明にstaticを放り込むようなことになって申し訳ないです。例の設計ミスです・・。しかし賢明な読者様なら乗り越えてくれると信じて強行突破します)
 つまり、何が言いたいかといえば、staticなフィールドの値をゲットするゲッターメソッドはstaticにするべきだということです。ゲッターをstaticにすることで、

Human.getCount_Human()

という使い方ができるようになるので、インスタンスを作っていなくてもこのメソッドを使えるわけです。なので、このゲッターをstaticにしておきましょう。

Human.java

public class Human{
        private static int count_Human = 0;
	String name;
        int birthday;
        int manpukudo;

        // ~コンストラクタ省略~

        public static int getCount_Human(){
                return count_Human;
        }

        void eat(){
                this.manpukudo += 60;
        }
}

 これでstaticなフィールドの値をゲットするstaticなゲッターが完成しました。さっそくこれを使ってcount_Humanをゲットしてみましょう。

HumanTest.java

public class HumanTest{
	public static void main(String[] args){
                System.out.println(Human.getCount_Human());

                Human human1 = new Human();
                System.out.println(Human.getCount_Human());
                
                Human human2 = new Human();
                System.out.println(Human.getCount_Human());
	}
}

 先ほどは、「Human.count_Human」でcount_Humanにアクセスしていた部分を「Human.getCount_Human()」に変えました。ゲッターはpublicにしてあるので、

クラス名.メンバ

の形で呼べます。なので普通に、

Human.getCount_Human()

として大丈夫です。その結果、戻り値としてcount_Humanの値が返ってくるわけです。実行結果はこうなります。

static1

 このように、フィールドをprivateにしてしまって、そのフィールド値を取得する為の(publicな)ゲッターを作ることによって、そのフィールド値は他のクラスから書き換えることはできないが、取得することはできる状態になったわけです。つまり整合性を保ちながらも、その値を引っ張り出すことはできる状態です。
 このアクセス修飾子とゲッター(セッター)の使い方は初心者には、なんとなく回りくどい、まどろっこしい感じがすると思います。実際、1人でプログラムを組んでいる限り、publicにしているからと言って誰かが勝手にその値を書き換えたりする危険にさらされるわけではありません。

Human.count_Human = 100;

こんな悪さをされないように、とは言うものの、するのは自分以外いないのですから、それを防ぐ為に策を講じるというのもなんか間抜けな話です。
 しかし、オブジェクト指向というプログラミング方法を実現する為にはこのアクセス制限という考え方が非常に重要なポイントになってきます。オブジェクト指向というものを感じる為にも、この一見まどろっこしいprivate&ゲッターをよく理解して使うようにして下さい。
 次回は、ゲッターの相方みたいな存在のセッターについて説明します。

sponsored link

Androidアプリを作ろう

コメント

  • それを防ぐにアクセス修飾子を使って→それを防ぐのに
    この変数の値をいじられるような事態をを防ぐために、→事態を防ぐために、
    以下はHumanテストと同パッケージ内にあるHumanTestクラスです。←何かが間違っているような

    by あさやん €2015/11/16 22:26

  • staticの説明の部分、大変参考になりました。

    とある事情で、インスタンス化することができないクラスのメンバ変数にアクセスしなければならず「static public変数にするしかないのかな?」と悩んでおりました。

    ありがとうございました!

    by Pi €2016/04/08 17:17

down

コメントする



CAPTCHA


一番かんたんなJava入門

Androidアプリの作り方

忘備録

私の作ったAndroidアプリ

私の作ったWordPressテーマ

プロに教わるオンライン学習

管理人

Nobuo_CREATE

Nobuo_CREATE

WordPressテーマPrinciple、マテリアルを作ったり、Androidアプリを作ったり、Java入門サイトを作ったり、本を書いたりしています。どうぞよろしく。 [詳細]



sponsored link

オススメ書籍

[オススメpoint]

 この本は全く何も分からない初心者の方にお勧めです。プログラミングをするには覚えなければならない事が無茶苦茶いっぱいありますが、この本は教えてくれる順番、その構成が素晴らしいです。RPGのゲームを作るというストーリーにのっとってちょっとずつ難しいことを教えてもらえます。
 無機質で膨大なデータが載っているような本は読む気にならないという方は、こういうストーリー仕立ての本でチャレンジしてみてはいかがでしょうか?(注:RPGを作る為の本ではありません。)

[オススメpoint]

 ある程度、Javaを読み書きできるようになったら、オブジェクト指向について学ぶべきです。本書は、抽象的で分かったような分からんようなオブジェクト指向について、非常に分かりやすい例を出して説明してくれています。オブジェクト指向とは何なのか?という本質を掴むのにこれほど適した本はないと思います。オブジェクト志向の理念を理解できれば、より効率のいいコードをより楽に書けるようになるはずです。Java上級者を目指すなら必読の一冊!

只今、急拡大中

ゲームを作りたい人専門

個人フットサル予約サイト

ガチ専門

JavaからのRuby on Rails入門

JavaからのRuby on Rails入門