一番かんたんなJava入門

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

【Java】 アクセス修飾子とゲッター

time 2013/07/10

sponsored link

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

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

 アクセス修飾子と言えば、publicというのがそれです。クラスの前に、

public class クラス名

という風につけたり、メソッドの修飾子としても、つけていましたよね。

public static void main(String[] args)

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

 表にするとこんな感じです。

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

 
 下に行くほどアクセス制限が強いわけですが、4つもあるとややこしいと思うので、初めはpublic(公開)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(){                                          // コンストラクタ2
                this("不明", 0, 50);
        }

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

 続けて読んでいただいている方は、見慣れたかもしれませんが、これは人間オブジェクトを作るためのHumanクラスです。Humanクラスにはご覧のように5つのメンバと2つのコンストラクタがあります。

 まず注目してもらいたいのが、staticなフィールドであるcount_Humanです。count_HumanはこのHumanクラスのオブジェクトを作られた数をカウントしているstaticな変数です。newされる度にカウントが一つ増えるようにコンストラクタ1の中にcount_Human++;を仕込んでいます。

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

参考:オブジェクトって何? 〔コンストラクタとは〕 〔this( )の意味〕

 以下はHumanクラスと同パッケージ内にある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に勝手におかしな値を代入されるのを防ぐために、アクセス修飾子を使います。

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.count_Human = 100;」とやれば普通に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);
	}
}

 HumanTestクラスでは、count_Humanの値の変移が分かるようにインスタンスを作ってはcount_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というのはどこからでもどうぞご自由にアクセスして下さいという意味のアクセス修飾子です。ゲッターとは他のクラスからそのクラスのパラメータを取得するためのメソッドなので、アクセス制限は不要です。

 ゲッターはゲットしたいフィールドの値を戻り値として返します。なのでその戻り値の型をメソッド名の前に書きます。ここではゲットしたいフィールドは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

コメントする



一番かんたんなJava入門

Androidアプリの作り方

忘備録

私の作ったAndroidアプリ

おすすめ入門書・サービス

おすすめプログラミングスクール

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

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

フリーランスで稼ぎたい人へ

管理人

Nobuo_CREATE

Nobuo_CREATE

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



sponsored link

オススメ書籍

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

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

只今、急拡大中

JavaからのRuby on Rails入門

JavaからのRuby on Rails入門

大人気!COBOLからのJAVA

ブロックチェーン入門

オン ザ・ブロックチェーン