【Java初心者用】『銀行口座クラス』でクラスの作り方を学ぶ
Javaでプログラムを作成するということは、『クラスを作成する』ということです。オブジェクト指向言語では、クラスは非常に重要な技術となります。
しかし、初心者にとっては「クラスを作成(設計)する」のは、非常に難しいことです。あなたもどのようにしてクラスを作成したらよいのか悩んでいませんか?
今回はクラスの作り方を『銀行口座』を題材に、初心者のためにじっくり解説していきます。
私は初心者を対象としたJavaのセミナーや職業訓練の担当をしています。Javaの講習でクラスを作成する時に悩んでいる初心者を数多く見ています。そこで、今までの経験を活かして、初心者のためにじっくりクラスの作り方を解説していきます。
まず、この記事は以下のような人を対象としています。
対象者・Javaを勉強中の初心者
・クラスが何かを知りたい人
・Javaでクラスの作り方を知りたい人
この記事を読むと、次のようなことが理解できるようになります。
この記事を読むとできること・クラスは何をするものか知ることができる
・カプセル化について知ることができる
・クラスを構成するクラスメンバ(フィールド、メソッド)について知ることができる
・コンストラクタの役割りや書き方を知ることができる
Javaでクラスを作りたいのですが、どのようにして作ったらよいかわかりません。
初心者の人はクラスを作成しようとすると悩む人が多いですよ。
やっぱり、難しいんですね。どのように勉強したら良いのでしょうか?
今回はクラスの役割りや作り方を説明します。
オブジェクト指向とは
オブジェクト指向と呼ばれるプログラム言語では、「プログラムを作る」ということは「クラスを作成する」ということです。
まず簡単にオブジェクト指向について説明します。
オブジェクト指向では、「オブジェクト(もの)」に注目してシステムを考えていきます。この「オブジェクト」とは「物理的に存在する物体」や「目に見えない処理」のことです。例えば、いま皆さんが利用しているパソコンやスマートフォンがオブジェクトであり、買い物をする時の支払い処理やチケットの予約処理がオブジェクトです。つまり、私たちの日常生活はほぼオブジェクトの集まりとなります。このオブジェクトに注目して、システムを作成していくのがオブジェクト指向となります。
それまでのプログラミングでは、どんな処理がどのような順番で処理されるのか「手続き」に注目していました。このようなプログラミング手法を「手続き型」と呼びます。
この2つの大きな違いは、使いまわしができるかどうかになります。手続き型の場合、作成するプログラムはそのシステム専用になり、他のシステム開発で流用することはあまり考慮しません。一方、オブジェクト指向の場合は、作成したクラスは他のシステム開発で流用することができます。
例えば、「支払い業務」という処理は、社員の給与を管理するシステムでもショッピングの会計システムでも存在する処理です。一度「支払い業務」というクラスを作成しておけば、色々なシステムで再利用することができます。オブジェクト指向では、このようにクラスを複数組み合わせてシステムを作成していきます。
よくオブジェクト指向の説明で使われる「車」について考えてみましょう。
車は「エンジン」「タイヤ」「ハンドル」「座席」「ライト」など色々な部品が組み合わされて作られています。これらの部品は、色々な会社で個別に製造され、製造ラインで一つにまとめられます。製造したエンジンは全く他の車種でも利用することができる場合があります。このように部品ひとつひとつが独立していて、そららを組み合わせてシステムを作成する考え方がオブジェクト指向となります。
クラスとは
オブジェクト指向のシステム内の個々の部品/処理が「クラス」となります。では、クラスとは具体的にどのようなものなのでしょうか?
クラスは「情報」と「機能」で構成されています。「情報」とは「クラスが持っている特徴」、「機能」は「クラスで行える処理」です。
具体的な例で説明します。先ほどの車のエンジンの場合で考えてみます。
エンジンには「排気量」「重さ」「大きさ」などの情報があります。また、「燃料を燃やす」「内部のタービンを動かす」などの機能があります。エンジンというクラスは、こられの情報で構成されていることになります。つまり、エンジンクラスとは実際にエンジンという「もの」を製造するための設計書です。この設計書を元に実際に工場でエンジンそのものを製造しています。
設計書さえあれば、同じエンジンを何個でも大量生産することができます。また、その設計書を他のエンジンの開発時に流用することもできます。
Javaのプログラムでキーボードからデータを入力させる時に「Scannerクラス」をよく利用します。これは、「データを入力する」という処理に注目して作成されたクラスです。このクラスには「整数を読み込む機能」や「文字列を読み込む機能」、「1行全体を文字列として読み込む機能」などが用意されています。
また、この「Scannerクラス」は色々なプログラムでいつでも利用することができる汎用的なクラスです。
このようにクラスは、オブジェクトに注目し、そのオブジェクトが持つ特徴(情報や機能)を具体的に記述したものとなります。
クラスの構成要素を図にしたものが以下の図です。それぞれについて、これから説明します。
クラスメンバ
クラスには、「情報」と「機能」があると説明しました。これらをJava言語では「フィールド」と「メソッド」と呼びます。これらをまとめて「クラスメンバ」と呼びます。
フィールド
クラスを作成する場合、まずそのオブジェクトにはどのような情報があるのかを考えます。考える時は「名詞」に注目してください。
エンジンの場合、「排気量」「重さ」「大きさ」が情報でした。これらに共通していることは、全て名詞で表現されていることです。したがって、クラスが持つ情報は名詞で表現されるものになります。
これらの名詞をJavaのクラスで表現する場合は、変数として記述します。フィールドという特別な名称が使われているので何か特別な変数なのか、と思うかも知れませんが通常利用する変数と変わりありません。変数をメソッドの外側に記述することが通常の変数と違う点になります。
メソッド
クラスを作成する場合、情報の他にそのオブジェクトでは何かできるのか、機能について考えます。考える時は「動詞」に注目します。
エンジンの場合、「燃料を燃やす」「内部のタービンを動かす」が機能でした。これらは動詞で表現されるものです。したがって、クラスが持つ機能は動詞で表現されるものになります。
これらの動詞をJavaのクラスで表現する場合は、メソッドとして記述します。メソッドを定義する場合、そのクラス内だけでしか利用できないのか、または他のクラス(外部)から利用できるようにするのかで書き方が違ってきます。
メソッドの書き方については以下の記事で詳しく説明しています。
カプセル化
クラスを作成する際に重要な考え方は「カプセル化」です。カプセル化はクラスメンバを勝手に悪用させないための仕組みとなります。仕組みと言いましたが、具体的には記述方法になります。
カプセル化を適切に利用していないクラスの場合、クラス外部からクラスメンバを操作されてしまいます。例えば、エンジンクラスの場合、排気量「2000cc」という情報を排気量「3000cc」に書き換えられてしまいます。また、キーを差し込んでから「燃料を燃やす」という機能を実行させたいのに、ドアを開けた瞬間に「燃料を燃やす」という機能を利用されてしまいます。このような不正な操作をされないようにクラスを保護しながら作成しておかなければなりません。
クラスを作成している人の意図と反することを勝手におこなわせないようにするのが「カプセル化」です。
具体的なルールは以下の通りです。
- フィールドは全て「private」を付けて定義する
- メソッドは外部から利用できるものは「public」、内部だけに限定する場合は「private」を付けて宣言する
- フィールドにアクセスする必要がある場合は、セッター/ゲッターを用意する
フィールドは全て「private」を付けて定義する
フィールドはクラスが持つ重要な情報です。したがって、外部から直接アクセス(書換え)できないように「private」を付けて宣言します。private宣言されたフィールドはそのクラス内でしかアクセスすることができません。
カプセル化では、フィールドは必ずprivate宣言し、外部からのアクセスを禁止させます。
フィールドにアクセスする必要がある場合は、セッター/ゲッターを用意する
フィールドは「private」を付けて外部参照を禁止します。しかし、外部からフィールドをアクセスしたい場合があります。そのような要望に応えるために「セッター/ゲッター」を用意します。セッター/ゲッターはフィールドに間接的にアクセスることができるメソッドです。
セッターは「フィールドに値を間接的に設定するためのメソッド」、ゲッターは「フィールドに設定されている値を間接的に取得するためのメソッド」です。
カプセル化では、フィールドに間接的にアクセスする必要がある場合、ゲッター/セッターを用意しておきます。
メソッドは外部から利用できるものは「public」、内部だけに限定する場合は「private」を付けて宣言する
クラス内にメソッドを定義する場合、他のクラスからもアクセスできるメソッドには「public」、自分自身のクラス内からのみアクセスできるメソッドには「private」をつけます。
カプセル化の目的な、クラス内部を適切に隠蔽、保護することです。したがって、メソッドを意味もなくpublic宣言して公開しないように注意しましょう。
サンプル:銀行口座クラス(クラスメンバ)
では、実際にサンプルとして「銀行口座クラス」を作成してみましょう。
銀行口座についてまず分析してみます。ここでは、説明を簡単にするために単純な内容のみ取り上げます。
銀行口座に必要な情報(フィールド)は何でしょう? 考えられるものは「氏名」「銀行番号」「支店番号」「口座番号」「残金」となります。一方、銀行で行う機能(メソッド)にはどんなものがあるでしょうか? 「残金を表示する(残高照会)」「お金を預け入れる」「お金を引き出す」「口座番号を確認する」の4つを今回は実装します。
洗い出した情報と機能に対応するフィールド名とメソッド名は以下の表のようにします。
次にメソッドについて考えていきます。
メソッドを考える場合、まず公開するかしないかを考えます。公開すると外部から直接そのメソッドを実行できます。今回用意するメソッドのうち、公開するメソッドは「残高照会」「預入処理」「引落処理」の3つです。「口座番号照会」は他のメソッドからのみ利用できるようにするので、直接外部から利用させません。
また、それぞれのメソッドについて、実行する際に何かデータが必要か、また処理した後に何かデータを返すのかを検討します。
まず、実行する際に何かデータ(引数)が必要かですが、「預金処理」と「引落処理」は金額の指定が必要です。一方「残高照会」と「口座番号照会」は今回は自分の口座を対象にするようにしますので、何もデータを必要としません。
次に処理後に結果(戻り値)が返されるかどうかですが、「預金処理」と「引落処理」は入金と引落をするだけで何も結果を返しません。一方「残高照会」は「口座番号と残金」、「口座番号照会」は「口座番号」を返します。
以上をまとめると作成するメソッドは、次のようになります。
ここまでの内容を実際にJavaで記述すると以下のようになります。なお、宣言/定義していても利用されていないため注意文が黄色い波線で表示されていますが今後コードを記述すれば消えますので気にしないでください。
セッター/ゲッター
クラス内に作成するメソッドのうち、外部からフィールドに値を代入するためのメソッドを「セッター」、フィールドから値を外部から取得するメソッドを「ゲッター」と呼びます。
フィールドはprivate宣言されているので直接外部からアクセスすることができません。しかし、外部から値を設定したい、設定されている値を取得したいという場合があります。そのような要望のために作成するメソッドがセッター/ゲッターです。
セッターはフィールドに値を設定するだけなので「書き込み専用」です。一方、ゲッターは値を取得するためだけなので「読み込み専用」となります。
サンプル:銀行口座クラス(セッター/ゲッター)
先ほど作成した、銀行口座クラスの中でセッター/ゲッターとしてどんなメソッドが必要でしょうか? セッター/ゲッターはprivate宣言されたフィールドの値を操作するためのメソッドです。BankAccountクラス内でのprivate宣言されたフィールドにアクセスしているメソッドは「deposit()」と「withdraw()」「getBalane()」の3つになります。
したがって、この3つのメソッドがセッター/ゲッターとなります。預入処理をする「deposit()」メソッドは指定された金額を残金「balance」に追加しているのでセッターとなります。引落処理をする「withdraw()」メソッドは「balance」から残金を取得し、残高照会をする「showBalance()」メソッドは「balance」から金額、「accountNubmer」からを口座番号をそれぞれ取得しているのでゲッターとなります。
これらは外部から利用できるようにするので、public宣言されています。今回他にセッター/ゲッターとして必要な処理はありません。
以上を踏まえて、クラスの構成を図にすると次のようになります。
コンストラクタ
クラスからインスタンスを作成する際に何らかの処理を行いたい場合、その処理は「コンストラクタ」に記述します。コンストラクタは「インスタンスを作成する時に1度だけ実行される特別な処理」です。コンストラクタはnew演算子を使ってインスタンスを作成すると実行されます。
ユーザがプログラム内の好きな場所でコンストラクタを意図的に実行させることはできません。
コンストラクタを定義する場合、注意することが幾つかあります。
コンストラクタ名はクラス名と完全に一致させる
コンストラクタを記述する場合、コンストラクタ名はクラス名と完全に同じ名前にしなければなりません。Javaはクラス内のどのメソッドがコンストラクタなのかを「クラス名と完全に同じかどうか」でチェックします。
戻り値のデータ型を記述してはいけない
コンストラクタを記述する場合、戻り値のデータ型を記述してはいけません。コンストラクタは通常、フィールドに値を設定したり、他のメソッドを実行したりするので、値を返さない場合があります。通常のメソッドの場合、値を返さなければ「void」と記述します。しかし、コンストラクタの場合、一切戻り値のデータ型を記述してはいけないので、値を返さない場合でも「void」を記述しません。「void」と記述すると、それはコンストラクタではなく、通常のメソッドとなります。
デフォルトのコンストラクタしかない場合は記述は不要
何もデータを受け取らず、何も処理を行わないコンストラクタを「デフォルト(暗黙)のコンストラクタ」と呼びます。クラスからインスタンスを作成する際、コンストラクタは必ず実行されます。しかし、何も処理する必要がない場合でもユーザがコンストラクタを明示的に記述しなければならないのは面倒です。そこで、何もデータを受け取らず、何も処理をおこなわないコンストラクタに限ってユーザは記述する必要がありません。デフォルトのコンストラクタはコードとして明示的に記述していなくても、内部であるものとして処理されます。
引数ありのコンストラクタを追加した場合、デフォルトのコンストラクタは消滅する
何かデータを受け取って、何らかの処理をおこなうコンストラクタを明示的に追加すると、デフォルトのコンストラクタは消滅します。もし、デフォルトのコンストラクタも利用したい場合は、ユーザが明示的にデフォルトのコンストラクタを記述しなければなりません。デフォルトのコンストラクタを利用する必要がなければ記述は不要です。
インスタンス作成時の初期値
クラスからインスタンスを作成するとフィールドには初期値が設定されます。データ型によって設定される初期値は以下の通りです。
データ型 | 設定される初期値 |
int | 0 |
double | 0.0 |
String | null |
boolean | false |
サンプル:銀行口座クラス(コンストラクタ)
銀行口座を新規開設する時に何か最初に行いたい処理があるかどうかを考え、ある場合それをコンストラクタで定義します。銀行口座は「氏名」と「口座番号」を指定して新規作成するとします。また、新規作成した時に「銀行番号」と「支店番号」も自動的に設定するようにします。
したがって、記述するコンストラクタは以下のもの(12行目から19行目)が考えられます。なお、今回引数なしのコンストラクタは必要ありません。引数なしコンストラクタは、開設した人の名前と口座番号を設定することができません。この銀行口座クラスは新規作成作成する際には氏名と口座番号の指定を必須とします。
サンプル:銀行口座クラスの実行例
今回作成した銀行口座クラスはエントリポイント(main()メソッド)がないため単体で実行することはできません。したがって、動作確認をおこなうためにはエントリポイントを含む別のクラスを作成する必要があります。確認用クラスとして「BankAccountMainクラス」を作成し、動作確認をおこなってみます。
以下が確認用のプログラム(BankAccountMain.java)です。
実行結果は以下の通りです。それぞれの番号は上のプログラムの番号部分を実行した結果を表しています。
最後に全体のソースコードを掲載しておきます。
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 |
package myClass; public class BankAccount { // フィールドの宣言 private String userName; private String bankNumber; private String branchNumber; private String accountNumber; private int balance; // コンストラクタの定義 public BankAccount(String name, String number) { this.userName = name; this.bankNumber = "A00"; this.branchNumber = "123"; this.accountNumber = number; this.balance = 0; } // 残高照会メソッドの定義 public void showBalance() { // 口座番号の取得 String number = getAccountNumber(); // 口座番号と残金をひとつの文字に連結して表示 System.out.println("口座番号:[" + number + "] 残金: [" + this.balance + "円]"); } // 預入処理メソッドの定義 public void deposit(int money) { // 残金を増やす this.balance += money; } // 引落処理メソッドの定義 public void withdraw(int money) { // 引落ができる残金があるか確認 if (balance - money >= 0) { // 引落可能であれば残金を減らす balance -= money; } } // 口座番号照会メソッドの定義 private String getAccountNumber() { // 口座番号を返す return this.accountNumber; } } |
確認用のBankAccountMain.javaのソースコードは以下の通りです。
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 |
package myClass; public class BankAccountMain { public static void main(String[] args) { // 新規銀行口座の開設 BankAccount myAccount = new BankAccount("日本太郎", "1234567"); // 開設直後の残高照会 myAccount.showBalance(); // 5000円の預入れ myAccount.deposit(5000); myAccount.showBalance(); // 2000円の預入れ myAccount.deposit(2000); myAccount.showBalance(); // 6000円の引落し myAccount.withdraw(6000); myAccount.showBalance(); // 10000円の引落し(残金が足りない) myAccount.withdraw(10000); myAccount.showBalance(); } } |
まとめ
今回は、Java言語でのクラスの作り方を『銀行口座クラス』をもとについて説明しました。Java言語ではプログラムを作成するということはクラスを作成することと同じです。クラスは非常に重要ですが、作成するためには色々なことに注意しなければなりません。
・クラスにしたいオブジェクトに注目し、情報を機能を洗い出す
・クラスはカプセル化を意識して定義する
・クラスには適切にセッター/ゲッターを用意する
・新規作成時に自動的におこなわせたい処理はコンストラクタに定義しておく
Javaではクラスの作成は非常に重要です。したがって、覚えなければならないこともたくさんあります。いかがでしたか?
盛りだくさんの内容で大変でした。
これからは気を付けながらプログラムを作ってみます。
直ぐに覚える必要はありません。少しずつ慣れて下さい!!
クラスの継承については、以下の記事で詳しく説明しています。合わせて確認してみて下さい。