【文系でもわかるプログラミング】4.クラスとメソッドの扱い方とコンストラクタ
ここまでのプログラムは基本的にはプログラムの上から下へと処理が流れていくものでした。そしてそのプログラムはmainメソッドと呼ばれる特別なメソッドの中に書かれていました。
しかしすべてのプログラムをmainメソッドに書くわけではありません。プログラムが大きくなればなるほど、クラスと呼ばれるものを複数作ってプログラムを動かしていきます。その分割の方法をオブジェクト指向と呼びます。
ここでは簡単なサンプルコードを作りながらその骨子を学んでいきます。
【PR】プログラミングスクール【PR】独学で挫折しそう、やっぱり難しい、手早く学びたい。そんな人にはやはりプログラミングスクールがお勧めです。
【プログラミングスクールを利用するメリット】
JavaはWebアプリケーションなどを作ることができるプログラミング言語です。独学だと基本事項からなかなか抜け出せず、アプリを作るイメージができずに挫折してしまう人も多いです。紹介する二つのプログラミングスクールのJavaコースはWebアプリケーションを作ることを前提にカリキュラムが組まれていますので、より実用的な内容を学ぶことができます。
おすすめ1▶TechAcademy
Javaコース
おすすめ1▶CodeCamp
Javaマスターコース
クラスの基本
Javaのコードは多数のクラスにより役割分担をされています。
用語「クラス」
データと処理をまとめたもので、オブジェクトを生成するための雛形となるもの。
【構文】
[修飾子] class クラス名{ //変数やメソッドなど }
クラスの概念はなかなか理解が難しいものです。クラスを知るためにはまずは変数やメソッドというものの理解が必要です。そのために簡単なサンプルプログラムを作りながら、説明をしていきましょう。
社員管理プログラムの概要
今回はありきたりな例ですが、「社員管理プログラム」というプログラムを作成してみましょう。概要は次のようになります。
【社員管理プログラムの概要】
・Companyクラス
mainメソッドを持つ実行用のクラス。Employeeクラスをインスタンス化し、データのやり取りを行い、出力する。
・Employeeクラス
社員データ(名前、所属部署、社員番号)を保持する。
つまり、データを入力したり出力したりするのはCompanyクラスですが、実際にデータを保持するクラスはEmployeeクラスにする、という役割分担をさせます。
まず、大枠としてはこのようなコードになります。
1 2 3 4 5 6 7 8 9 10 11 |
public class Test { public static void main(String[] args) { //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 //社員データを格納するメソッド } |
この例ではクラスを二つ作って役割分担をさせます。クラス名は通常は役割がわかるような名前を付けます。
Employeeクラスの作成
それでは、社員のデータを格納するEmployeeクラスを作成したいと思います。
Employeeクラスにはname、section、numberの3つの変数があり、ここにそれぞれ社員名、所属部署、社員番号を格納します。また、EmployeeクラスにはsetEmployeeメソッドがあり、これは3つの変数にデータをセットするためのメソッドです。
これらクラスの直下にある変数とメソッドをメンバと呼びます。特にメンバの変数のことをメンバ変数又はフィールドと呼び、今後よく出てくる用語ですのでよく覚えてください。
用語「メンバ」クラス直下に宣言する変数とメソッドを指すことば。
用語「フィールド」メンバのうち、変数のこと。メンバ変数ともいう。
では具体的にこれらメンバを定義していきます。
変数を宣言する
ひとつひとつ変数を宣言していきます。そのためにはデータ型が必要ですので、次のようにデータ型を決めていきます。
社員名 name・・・「太郎」のような文字列にするのでString型。
所属部署 section・・・「システム部」のような文字列にするのでString型。
社員番号 number・・・10のような数値にするためint型。
データ型が決まったら並べていきましょう。今回は変数に最初に数字は代入しません(初期化はしません)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Company { public static void main(String[] args) { //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド } |
メソッドを記述する
3つの変数にデータをセットするsetEmployeeメソッドを記述します。
用語「メソッド」データを処理するための機能を記述したブロック。メソッドはプログラム内で呼び出すことが必要であることから、処理をする本体であるメソッド(呼び出し先)に加えて呼び出し元も記述する必要がある。
【構文】
[修飾子] 戻り値の型 メソッド名(引数リスト){//処理を記述する}
まずは社員名だけ格納するメソッドを作ります。変数にデータを格納するというメソッドはsetterと呼ばれます。よく使われるのでしっかり見ていきましょう。
メソッドの引数に社員名を受け取る変数name記述して、受け取るようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
setEmployee(String public class Company { public static void main(String[] args) { //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n){ //処理を記述する } } |
15行目の「void setEmployee(String n){ //処理を記述する }」の部分がメソッドです。
まず、このメソッドは戻り値がないので先頭にvoidを記述します。
用語「戻り値」メソッドの処理の結果。メソッドの処理のブロックの最後にreturn文を書くことにより処理の結果を返すことができる。戻り値がないメソッドも記述することができ、その場合の戻り値の型はvoidとなる。
「変数に値をセットするのって戻り値じゃないの?」という疑問もあろうと思います。私も最初はこの戻り値の意味がよく分からず混乱しました。
戻り値というのはメソッドの”呼び出し元”に返す処理結果です。今回のプログラムは、メソッドの“呼び出し元”をCompanyクラスに記述する予定です。つまり、戻り値がある場合はCompanyクラスに書いたメソッドの呼び出し元に返ります。ところが、setEmployeeメソッドはEmployeeクラスにある変数に値をセットするだけのメソッドです。よって、このメソッドには戻り値がありません。
メソッド名のあとには引数リストを記述します。今回、このメソッドはString型の「名前」を引数として受け取って、メソッドの処理によってメンバ変数にセットします。ここでは「(String n)」としましたが、変数nはなんでも大丈夫です。
それでは、{ }内の処理を記述しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Company { public static void main(String[] args) { //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n){ name = n; //引数nをメンバ変数nameに格納する } } |
setterであるsetEmployeeメソッドは単にメンバ変数に値をセットするだけのメソッドですので、処理はこれだけです。代入演算子である「=」は右辺の値を左辺に代入します。つまり、引数nとして受けたデータが左辺のnameに格納され、それがメンバ変数のnameにセットされます。
メソッドの引数リストはいくつでも引数を記述することができます。つまり、setterはいくつでもまとめて値を変数にセットすることができます。このプログラムは所属部署と社員番号もセットする必要があるので、全部まとめてセットすることにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Company { public static void main(String[] args) { //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
これでEmployeeクラスは変数とメソッドというメンバを持つ典型的なクラスになりました。これでEmployeeクラスは完成です。
Companyクラスを作成する
ここからが初めてだと少しわかりにくい部分です。Employeeクラスを使うために、Companyクラスを作成していきましょう。
CompanyクラスはEmployeeクラスと大きく違った性格を持つクラスで、mainメソッドを持っています。mainメソッドはもう何度も使っていると思いますが、これもメソッドの一種です。ただ、Javaのプログラムを実行したときに最初に呼び出される部分ですので、特殊なメソッドです。
全ての始まりはmainメソッドから
mainメソッドは実行したときに最初に呼び出されるメソッドです。mainメソッドを持つクラスはJavaの実行用のクラスとも捉えることができます。また、mainメソッドのあるクラスはいわゆる”神様クラス”として、いろいろと命令を出すクラスですので、ここで実践してみましょう。
インスタンス化
CompanyクラスからEmployeeクラスのメソッドであるsetEmployeeメソッドを使用します。そのためにはまずインスタンス化が必要となります。
用語「インスタンス化」クラスを基にオブジェクトを生成すること。オブジェクトを生成することによってはじめてクラスの中の変数やメソッドを使うことができるようになる。
【構文】
new クラス名();
用語「オブジェクト」メモリ上に生成された、Javaのプログラムの分割の単位。クラスを基にインスタンス化をすることで生成されるデータと処理の集まり。
この辺で用語がごっちゃごちゃになってきて、何のことやらということになってきます。
まず、オブジェクトとインスタンスはほぼ同義です。これは何かというと、パソコンのメモリ上にある空間のことで、これがあることでプログラムが動きます。つまりプログラムの実体といえます。それに対し、クラスは単なるプログラムのコードであり、オブジェクトを生成するための設計図や雛形ともいわれます。
クラスがプログラムのコード上にあったところでメモリの上にオブジェクトが生成されていないとプログラムは動かないのです。オブジェクトを生成するためにはJavaの場合はそのためのコードが必要なことになっており、そのコードによってオブジェクトが生成されることをインスタンス化といいます。
それでは、実際にEmployeeクラスをインスタンス化しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class Company { public static void main(String[] args) { new Employee(); //Employeeクラスをインスタンス化 //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
Employeeクラスをインスタンス化するので「new Employee();」とします。これでオブジェクトを生成できたのですが、これだけでは実際に使うことはできません。生成したオブジェクトを使用するためには、クラス型の変数を用意する必要があります。
用語「クラス型変数」参照型変数の一つで、クラスを定義することによって使用可能になる変数の型。
これまで使ってきた基本型やString型(参照型の一つ)はJavaが標準で備えている型です。それに対してクラス型は、プログラムを作成することによって作ることができ、プログラマーによってどんどん増やすことができる型です。クラス型によって宣言されている変数のことをクラス型変数と呼びます。
例えば、Employeeクラスをプログラムに書くことによってEmployee型を使えるようになりました。このEmployee型はEmployeeクラスをインスタンス化したオブジェクトを格納する変数として使用します。クラス型の変数にインスタンス化したオブジェクトを代入する構文は次の通りです。
【構文】
クラス型 変数名 = new クラス名();
そしてこの構文が一般的にインスタンス化の構文とされています。それでは、実際にプログラムを書いていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 //社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
これでインスタンス化ができてEmployeeのメソッドが呼び出せるようになりました。
メソッドの使用
次は、EmployeeクラスのsetEmployeeメソッドを使ってデータをセットしていきます。
メソッドを使うことをメソッドの呼び出しということがあります。メソッドの説明でも書きましたが、メソッドを使用するためにはメソッドの呼び出しが必要で、それをこれから記述していきます。メソッドの呼び出しは基本的には非常に簡単で、メソッド名を使って次のように記述します。
【構文】
メソッド名(引数リスト);
ただし、呼び出し元とは別のクラスにあるメソッドを使用するためには、オブジェクトが格納されている変数を指定して、利用するメソッドを特定する必要があります。Employeeクラスで先ほどインスタンス化したオブジェクトはクラス型変数e1に格納されています。そこで、次のように指定してメソッドを呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 e1.setEmployee("東京太郎","システム部",1902031);//社員データを格納する //社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
これでEmployeeクラスのオブジェクトに社員データがセットされました。
変数の使用
最後にEmployeeクラスに格納したデータを出力します。
Employeeクラスにセットしているメンバ変数を使うには、クラス型変数名にインスタンス変数h1を付けて記述します。それをSystem.out.printlnメソッドに記述すると表示されます。ここでは文字列「名前」「所属部署」「社員番号」も一緒に出力するようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 e1.setEmployee("東京太郎","システム部",1902031);//社員データを格納する System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number);//社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
所属部署:システム部
社員番号:1902031
社員管理プログラムの完成です。ここからはこのプログラムを改造しながら、インスタンス化の理解を深めていきましょう。
複数人のデータをインスタンス化
クラスをインスタンス化して使用するというプログラムの仕組みは一つのクラスを何回も利用するとより役に立ってきます。
例えば、この会社には新入社員が7人入ったとしましょう。その場合は、インスタンス化とメソッドを使った格納を7回ずつすることにより7人分のインスタンスを生成することができます。ではそのコードを見てみましょう。ちょっと長いコードになりますが仕組みは単純です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public class Company { public static void main(String[] args) { Employee e1 =new Employee(); e1.setEmployee("東京太郎","システム部",1902001); Employee e2 = new Employee(); e2.setEmployee("埼玉花子","営業部",1902002); Employee e3 = new Employee(); e3.setEmployee("千葉次郎","システム部",1902003); Employee e4 = new Employee(); e4.setEmployee("神奈川百子","総務部",1902004); Employee e5 = new Employee(); e5.setEmployee("群馬三郎","営業部",1902002); Employee e6 = new Employee(); e6.setEmployee("栃木四郎","営業部",1902004); Employee e7 = new Employee(); e7.setEmployee("茨城洋子","管理部",1902002); System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number); System.out.println("名前:"+e2.name); System.out.println("所属部署:"+e2.section); System.out.println("社員番号:"+e2.number); System.out.println("名前:"+e3.name); System.out.println("所属部署:"+e3.section); System.out.println("社員番号:"+e3.number); System.out.println("名前:"+e4.name); System.out.println("所属部署:"+e4.section); System.out.println("社員番号:"+e4.number); System.out.println("名前:"+e5.name); System.out.println("所属部署:"+e5.section); System.out.println("社員番号:"+e5.number); System.out.println("名前:"+e6.name); System.out.println("所属部署:"+e6.section); System.out.println("社員番号:"+e6.number); System.out.println("名前:"+e7.name); System.out.println("所属部署:"+e7.section); System.out.println("社員番号:"+e7.number); } } class Employee{ String name; String section; int number; void setEmployee(String n,String sec,int num) { this.name=n; this.section=sec; this.number=num; } } |
このコードではEmployeeクラスという「設計図」に基づいて、7つのオブジェクト(インスタンス)が生成されます。これがクラスが「設計図」または「雛形」といわれる理由です。一つのクラスについて、メモリ容量が許す限りいくつでもオブジェクトが生成されるのです。そしてe1、e2といった変数にはオブジェクトがあるメモリの番地が格納され、その変数を見る(参照する)ことによってデータを引き出すことができます。
ここまで、クラスやメソッド、インスタンス化について見てきました。ここからは少し応用的ですが基本的事項であるコンストラクタについて見ていきましょう。
コンストラクタ
コンストラクタはJavaの基本書ではメソッドとインスタンス化の説明の後にすぐ出てくる基本的事項ですが、メソッドとインスタンス化が理解できていないと混乱をきたす仕組みです。そういった意味ではやや応用的な事項に属すると思います。ここからはメソッドとインスタンス化が理解できている前提で話を進めていきます。
コンストラクタとは
まずは用語の説明をしましょう。
用語「コンストラクタ」インスタンス化されたときに最初に呼び出されるブロック。データの初期化を行うために使われる。
【構文】
[修飾子]コンストラクタ名(引数リスト){//処理を記述する }
※コンストラクタ名はクラス名と同じ
構文はメソッドと似ているため、最初はメソッドとの見分けがつかないこともありますが、コンストラクタには特徴があるので理解すれば容易に見分けがつきます。
【メソッドとコンストラクタの違い】
・名前がクラス名と同じこと
・戻り値がないこと(int、String、voidなどがつかない)
コンストラクタはインスタンス化の時に最初に呼び出されるので、データを初期化する際に利用されます。「コンストラクタ=初期化に使う」と覚えましょう。
社員管理プログラムにコンストラクタを導入する
実際に社員管理プログラムにコンストラクタを導入してコードを見ていきましょう。
この場合、初期化のためのデータはインスタンス変数に格納される社員データとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee("東京太郎","システム部",1902031); //Employeeクラスをインスタンス化 System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number);//社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するコンストラクタ Employee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
所属部署:システム部
社員番号:1902031
mainメソッドでEmployeeクラスをインスタンス化した時に、引数を3つ指定しています。この3つの引数がコンストラクタEmployeeに渡されて、Employeeオブジェクトのメンバ変数に格納されます。
繰り返しになりますが、動きはメソッドとそっくりですが、インスタンス化した時にすぐさま処理がなされるのがコンストラクタです。
これまで「インスタンス化→メソッドを使って変数にデータを格納」と2段階でデータを格納していました。コンストラクタを使うと「インスタンス化」の段階で変数にデータを格納できるため、簡単にデータを初期化できます。
ここまでで基本的なメソッドの扱い方は終わりですが、実際にJavaのBronzeやSilverの問題を見るとけっこう複雑でわかりにくいです。特に「クラス型変数」をメソッドの引数にしたときの挙動はわかりにくいです。これに関しては次の記事で紹介していますので、参考にしてください。
Static領域を使う
ここまで設計図であるクラスを基にオブジェクト(インスタンス)を作るというプログラムを学んできました。
復習になりますが、オブジェクトは一つのクラスからメモリの許す限りいくつでも作ることができます。それを試してみたのが「社員管理プログラム2」で、7人の新入社員オブジェクトを作り全て出力してみました。7人の新入社員オブジェクトは一つのクラスから作られていますが、全て別々のオブジェクトとしてメモリの領域が確保されています。つまり7領域のオブジェクトがメモリ上にあり、それぞれ別々の名前、所属部署、社員番号を持っているということです。
ここからはこれらバラバラに作られるオブジェクトとは少し毛並みの違った領域の話をします。それがstatic領域です。static領域は静的領域と日本語で訳されることがありますが、訳したところで何らわかりやすくはないのでここでは単にstatic領域といいます。
static領域とは
まずは用語の説明から始めます。
用語「static領域」オブジェクトとは別に、クラスに対して用意される領域。static変数とstaticメソッドがあり、クラスに共通した処理を行わせることができる。
これもまたすぐには理解しにくい事項ですが、ここではわかりやすいstatic変数について紹介していきたいと思います。
社員管理プログラムにstatic変数を導入する
社員管理プログラムにstatic変数を導入してみましょう。static変数は、例えば新入社員が何人か数えるときに使うことができます。
Employeeクラスをインスタンス化して、コンストラクタで3つのインスタンス変数のデータを初期化します。その時に、static変数を一つカウントアップするというプログラムを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public class Company { public static void main(String[] args) { //7人分の新入社員オブジェクトを生成 Employee e1 =new Employee("東京太郎","システム部",1902001); Employee e2 = new Employee("埼玉花子","営業部",1902002); Employee e3 = new Employee("千葉次郎","システム部",1902003); Employee e4 = new Employee("神奈川百子","総務部",1902004); Employee e5 = new Employee("群馬三郎","営業部",1902002); Employee e6 = new Employee("栃木四郎","営業部",1902004); Employee e7 = new Employee("茨城洋子","管理部",1902002); System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number); System.out.println("名前:"+e2.name); System.out.println("所属部署:"+e2.section); System.out.println("社員番号:"+e2.number); System.out.println("名前:"+e3.name); System.out.println("所属部署:"+e3.section); System.out.println("社員番号:"+e3.number); System.out.println("名前:"+e4.name); System.out.println("所属部署:"+e4.section); System.out.println("社員番号:"+e4.number); System.out.println("名前:"+e5.name); System.out.println("所属部署:"+e5.section); System.out.println("社員番号:"+e5.number); System.out.println("名前:"+e6.name); System.out.println("所属部署:"+e6.section); System.out.println("社員番号:"+e6.number); System.out.println("名前:"+e7.name); System.out.println("所属部署:"+e7.section); System.out.println("社員番号:"+e7.number); //Employeeクラスのstatic変数を出力 System.out.println("新入社員"+Employee.count+"名"); } } class Employee{ String name; String section; int number; static int count;//新入社員の数を数えるstatic変数 //新入社員のデータを格納するコンストラクタ Employee(String n,String sec,int num) { this.name=n; this.section=sec; this.number=num; count++; } } |
所属部署:システム部
社員番号:1902001
名前:埼玉花子
所属部署:営業部
社員番号:1902002
名前:千葉次郎
所属部署:システム部
社員番号:1902003
名前:神奈川百子
所属部署:総務部
社員番号:1902004
名前:群馬三郎
所属部署:営業部
社員番号:1902002
名前:栃木四郎
所属部署:営業部
社員番号:1902004
名前:茨城洋子
所属部署:管理部
社員番号:1902002
新入社員7名
もともとある3つのメンバ変数についてはそれぞれのオブジェクトにあり、データが格納されています。それに対し、static変数であるcountはstatic領域に一つだけあり、インスタンス化されるたびにcount++によって一つずつカウントアップされていきます。そして、最終的に新入社員の人数がわかるという仕組みです。
インスタンス化してオブジェクトが一つ増えるたびにどんどんstatic変数が更新されていくイメージです。
オブジェクトとstaticの違いは最初はイメージがしにくいものです。基本書でも説明が不十分でいったい何を言っているのかわからない場合がよくあります。オブジェクトは一つのクラスでたくさん作れる、staticはクラスに一つだけあるという基本をしっかり押さえて、先に進みましょう。
カプセル化
社員管理プログラム1ではsetEmployeeメソッドというメソッドを使ってEmployeeクラスの変数にデータをセットしました。また、変数にデータをセットするメソッドをsetterと呼ぶことを説明しました。
しかし、実はこのプログラムはsetterを使わずに、このように直接変数にデータをセットすることができます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 //社員データをEmployeeクラスの変数に直接代入する e1.name="東京太郎"; e1.section="システム部"; e1.number=1902031; System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number);//社員データを出力する } } class Employee{ //社員データを格納する変数 String name; //名前 String section; //所属部署 int number; //社員番号 //社員データを格納するメソッド void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
所属部署:システム部
社員番号:1902031
このプログラムはsetEmployeeメソッドを使用せず、直接Employeeクラスのメンバ変数にデータを代入していますが、実行結果は同じです。このように、メソッドの時と同じようにクラス型変数名を指定することにより、メンバ変数にも値を代入することができるのです。
ではどうしてsetterなどというメソッドが存在するのでしょうか。それはJavaをはじめとするオブジェクト指向言語が推奨するルールがあるからです。
カプセル化とは
オブジェクト指向にはカプセル化という考え方があります。
用語「カプセル化」オブジェクト指向プログラミングにおいて、クラス内の変数とメソッドを一体に扱い、他のクラスからのアクセスを制御すること。特に他のクラスから変数へのアクセスを制限(隠蔽)し、メソッド経由で変数の操作をさせること。
カプセル化は必ずしも決まった文法があるわけではなく、クラスをたくさん作ってプログラムを構築する中で実現させていくものです。ここまでクラスやメソッドなど文法を学んできましたが、カプセル化はそのようなものとは少し違ういわば設計思想のようなもので、実現には綿密な設計が必要となります。
このプログラムは短いものなので厳密なカプセル化は実現できませんが、少しだけカプセル化に近づけてみます。
カプセル化では変数にはprivate修飾子、メソッドにはpublic修飾子を付けることが推奨されていて、それがカプセル化にとって最も重要な考え方ですのでまずはそのように書き換えてみましょう。
修飾子とは
修飾子という用語は今までも目にしたことはあったと思いますが、やんわりと無視し続けてきたのでここで説明します。
用語「修飾子」クラス、変数、メソッドへのアクセスを制御するために付けるキーワード。修飾子を付けることにより他クラスや他パッケージからのアクセスを制限することができる。
つまり、private修飾子を付けると他のクラスからのアクセスができなくなります。アクセスができなくなるとはどういうことなのか、実際に先ほどのコードを使ってやってみましょう。
カプセル化の方針に従ってEmployeeクラスのメンバ変数にprivate修飾子、setEmployeeメソッドにpublic修飾子を付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 //社員データをEmployeeクラスの変数に直接代入する e1.name="東京太郎"; e1.section="システム部"; e1.number=1902001; System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number);//社員データを出力する } } class Employee{ //社員データを格納する変数 private String name; //名前 private String section; //所属部署 private int number; //社員番号 //社員データを格納するメソッド public void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
実行結果
フィールド Employee.name は不可視です
フィールド Employee.section は不可視です
フィールド Employee.number は不可視です
フィールド Employee.name は不可視です
フィールド Employee.section は不可視です
フィールド Employee.number は不可視です
すると、「Employee.nameは不可視です」といったエラーが出てきて、コンパイルができません。これがまさにアクセスができないという状態です。具体的には、6行目の「e1.name」としているのはCompanyクラスですので、このクラスからprivate指定されている変数nameへはアクセスができません。変数section、numberも同様です。
そこで使用するのがsetEmployeeメソッドです。このメソッドはpublic修飾子ですので、Companyクラスからのアクセスが可能です。setEmployeeメソッドはEmployeeクラスですので、同じクラスのメンバ変数であるname、section、numberにはアクセス可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 e1.setEmployee("東京太郎","システム部",1902031);//社員データを格納する System.out.println("名前:"+e1.name); System.out.println("所属部署:"+e1.section); System.out.println("社員番号:"+e1.number);//社員データを出力する } } class Employee{ //社員データを格納する変数 private String name; //名前 private String section; //所属部署 private int number; //社員番号 //社員データを格納するメソッド public void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } } |
実行結果
フィールド Employee.name は不可視です
フィールド Employee.section は不可視です
フィールド Employee.number は不可視です
これでエラーが半分になりましたが。しかし6行目からのprintlnメソッドでの出力がEmployeeクラスのprivate変数を指定しているので同じエラーが出ています。これからこのエラーを取っていきます。
CompanyクラスからEmployeeクラスのprivate変数を取ってきたいわけですが、直接取れなくなってしまいました。そこで活躍するのが変数を取ってくるだけのメソッドgetterです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 e1.setEmployee("東京太郎","システム部",1902031);//社員データを格納する System.out.println("名前:"+e1.getName()); System.out.println("所属部署:"+e1.getSection()); System.out.println("社員番号:"+e1.getNumber());//社員データを出力する } } class Employee{ //社員データを格納する変数 private String name; //名前 private String section; //所属部署 private int number; //社員番号 //社員データを格納するメソッド public void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する number = num; //引数numをメンバ変数numberに格納する } //社員データを返すメソッド public String getName() { return name; } public String getSection() { return section; } public int getNumber() { return number; } } |
所属部署:システム部
社員番号:1902031
これでエラーがなくなり、社員管理プログラム1と同じ出力結果になりました。これがカプセル化版の社員管理プログラムです。
カプセル化をする意味
カプセル化は他クラスから変数へのアクセスを制限することにより変数のデータを守るという意味があります。
実のところ、setterを付けてしまうとsetterを経由していくらでも変数が操作できてしまうので、あまり意味がないとも言えます。私もそういう疑問があって調べてみたのですが、それはごもっともだそうで、厳密なカプセル化を実現するためにはやたらとsetterを作ってはいけないようです。
ただ、例えばこうしてみれば変な操作を少しは防げます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class Company { public static void main(String[] args) { Employee e1 = new Employee(); //Employeeクラスをインスタンス化 e1.setEmployee("東京太郎","システム部",1902000);//社員データを格納する System.out.println("名前:"+e1.getName()); System.out.println("所属部署:"+e1.getSection()); System.out.println("社員番号:"+e1.getNumber());//社員データを出力する } } class Employee{ //社員データを格納する変数 private String name; //名前 private String section; //所属部署 private int number; //社員番号 //社員データを格納するメソッド public void setEmployee(String n,String sec,int num){ name = n; //引数nをメンバ変数nameに格納する section = sec; //引数secをメンバ変数sectionに格納する //1902001~1902999の番号だけ受け付ける if(num>=1902001&&num<=1902999) { number = num; } else{ System.out.println("※※注意※※"+num+"の社員番号はセットできませんでした");//引数numをメンバ変数numberに格納する } } //社員データを返すメソッド public String getName() { return name; } public String getSection() { return section; } public int getNumber() { return number; } } |
名前:東京太郎
所属部署:システム部
社員番号:0
社員番号をセットする部分にif文を挿入し、192001~1920999の範囲以外の数字がセットされないように制御しています。それ以外の数字がセットされたときは「※※注意※※1902000の社員番号はセットできませんでした」のように警告文を出すようにしています。社員番号が「0」になっているのは、インスタンス変数の初期値が「0」だからです。
サンプルコードは示しませんが、所属部署に入れる値を「システム部」「製造部」「総務部」「管理部」に限定するということもできるでしょう。
このように、カプセル化のプログラムはただやみくもにデータを変更するのではなく、メソッドによる操作を通して適切にデータを変更できるようにします。そして、綿密に設計をされた安全なプログラムを目指します。
まとめ
最後にここで紹介したサンプルコードをまとめておきたいと思います。
【社員管理プログラム1】クラスとメソッドを使った基本的なプログラム
【社員管理プログラム2】内容は社員管理プログラム1と同じだが、複数人分のオブジェクトが作成できることを確認したプログラム
【社員管理プログラム3】setterをなくし、コンストラクタを作成したプログラム
【社員管理プログラム4】static変数を利用し、オブジェクト化した社員の人数を数える機能を搭載したプログラム
【社員管理プログラム5】メンバ変数にprivate修飾子、メソッドにpublic修飾子を付け、カプセル化に近づけたプログラム
以上のプログラムを試しながら、様々な用語を紹介してきましたので一覧できるようにしておきます。
重要用語クラス、メンバ、フィールド、メソッド、戻り値、インスタンス化、オブジェクト、クラス型変数、コンストラクタ、static領域、カプセル化、修飾子
以降、継承やインタフェースなどJavaのクラスを支える重要な機能を学んでいきますが、それはこれらの理解が前提になっています。しっかり押さえていきましょう。
最後にこの記事を執筆する際に参考にしたものの中でおススメの書籍を紹介します。
ディスカッション
コメント一覧
まだ、コメントがありません