[Java] 8. クラスの継承とthis、superキーワードの使い方


Study / Java    作成日付 : 2019/08/05 23:22:58   修正日付 : 2020/12/23 18:49:43

こんにちは。明月です。


この投稿はクラスの継承とthis、superキーワードの使い方に関する説明です。


以前にクラスを作成する方法に関して投稿しました。

link - [Java] 7. クラスを作成する方法(コンストラクタを作成方法)


クラスは一つの目的をおいて構成すると説明しました。でも仕様によって様々なクラスをオブジェクトを作れば似ているクラスを多数に作ることを見れることがあります。

以前の投稿で説明した家計計算クラスで例を説明したことがありますが、それを参照して説明します。

Copy!
 [Source view] Privatefinance.java
import java.util.List;
import java.util.ArrayList;
// 家計簿クラス
public class Privatefinance {
// メンバー変数
// 入金のリスト
private List<Integer> input;
// 出金のリスト
private List<Integer> output;
// コンストラクタ
public Privatefinance() {
// リストを割り当て
this.input = new ArrayList<>();
this.output = new ArrayList<>();
}
// 入金関数
public void input(int money) {
// メンバー変数に金額を追加
this.input.add(money);
}
// 出金関数
public void output(int money) {
// メンバー変数に金額を追加
this.output.add(money);
}
// inputリストのデータを取得関数
protected int getInput(int i) {
// inputリストのデータをリターン
return input.get(i);
}
// outputリストのデータを取得関数
protected int getOutput(int i) {
// outputリストのデータをリターン
return output.get(i);
}
// 計算関数
public int calculator() {
// 結果のための変数
int sum = 0;
// リストに格納したすべての入金金額を足す。
for (int i = 0; i < input.size(); i++) {
sum += input.get(i);
}
// リストの格納したすべての出金金額を引き。
for (int i = 0; i < output.size(); i++) {
sum -= output.get(i);
}
// 結果をリターン
return sum;
}
}

Privatefinanceのクラスは入金、出金に関数クラスです。基本的に入金、出金を貰って最終にcalculator関数を通って値段を計算することです。

でも、ここで仕様によって通帳の内訳の機能があるクラスを追加したいです。つまり、クラスの内容は基本的に入金、出金は同じですが、入金、出金の順番と内容がある内訳が必要です。

ここで条件は上のクラスはもうサービスで運用しているし、もう検証が終わったクラスなので、できれば修正したくないです。なぜなら、修正があるとまた検証のためにテストが必要からです。上のクラスはすごく簡単のクラスだから少し修正しても問題ないですが、もっと複雑な仕様だと思えば理解すると思います。


その条件にするとすごく簡単にする方法は上のクラスをコピペしてクラス名を変更して作成すればよいです。また、他の仕様が追加すれば、そのことに続けてクラスをコピペで追加すればよいです。

でも、Javaの仕様によってListクラスは使用禁止になりました。その可能性はほぼ0パーセントになりますが、例えばのとして考えます。

それば問題がすべてのクラスのメンバー変数のinputとoutputのデータタイプを変更しなければならないです。そのクラスが20個~30個にするとすべて修正してすべてテスト及び検証しなければならないです。

実際のプロジェクトで本当にその作業することが多いです。


でも、上の問題はJavaの継承の機能を利用すればすごく簡単に解決します。

Copy!
 [Source view] Account.java
import java.util.ArrayList;
import java.util.List;
// Accountクラスを作成、Privatefinanceクラスを経書
public class Account extends Privatefinance {
// インラインクラス、つまり、クラス内部だけ使えるクラス。
private class Context {
String context;
int type;
}
// 通帳内訳を格納する変数
// 変数の割り当てはコンストラクタですべきが、直接メンバー変数に宣言を可能。
private List<Context> context = new ArrayList<>();
// 上のContextクラス生成関数
private Context newContext(String context, int type) {
// Contextクラス生成
Context c = new Context();
// 内訳
c.context = context;
// タイプ
c.type = type;
// 生成されたクラスをリターン
return c;
}
// 入金関数(以前クラスでは金額だけ入れるが、内訳内容も追加して再定義)
public void input(String context, int money) {
// Contextインスタンス生成
Context c = newContext(context, 1);
// 内訳追加
this.context.add(c);
// Privatefinanceクラスのinput関数を呼び出す。
super.input(money);
}
// 出金関数(以前クラスでは金額だけ入れるが、内訳内容も追加して再定義)
public void output(String context, int money) {
// Contextインスタンス生成
Context c = newContext(context, 2);
// 内訳追加
this.context.add(c);
// Privatefinanceクラスのoutput関数を呼び出す。
super.output(money);
}
// 出力関数
public void print() {
// ヘッダー出力
System.out.println("No\tContext\t\t入金\t\t出金");
// すべての内訳の順番はcontextリストにある。
// forの変数初期値は複数変数宣言も可能。
for (int inputIndex = 0, outputIndex = 0, i = 0; i < context.size(); i++) {
// contextリストにからContextインスタンスを取得
Context c = context.get(i);
int money = 0;
if (c.type == 1) {
// 入金リストから金額を取得
money = getInput(inputIndex);
// 入金リストのインデクス増加
inputIndex++;
// コンソール出力
System.out.println((i + 1) + "\t" + c.context + "\t\t" + money + "\t\t");
} else {
// 出金リストから金額を取得
money = getOutput(outputIndex);
// 出金リストのインデクス増加
outputIndex++;
// コンソール出力
System.out.println((i + 1) + "\t" + c.context + "\t\t" + "\t\t" + money);
}
}
}
// 実行関数
public static void main(String... args) {
// Accountインスタンスを生成
Account account = new Account();
// 通帳入金
account.input("給料", 100000);
// 通帳出金
account.output("家賃", 30000);
// 通帳出金
account.output("税金", 50000);
// 通帳内訳
account.print();
// 改行
System.out.println();
// コンソール出力
System.out.println("総額 : " + account.calculator());
}
}


上のAccountクラスはPrivatefinanceのクラスを継承しました。

クラスを継承する時にはextendsのキーワードを使います。Javaの継承ルールは二つ以上のクラスの複数継承は禁止されています。つまり、一つだけのクラスを継承することができます。

継承の意味はAccountクラスでPrivatefinanceのクラスのすべての機能を使えることができるという意味です。


上の例をみれはmain関数でAccountクラスにはcalculator関数がないので呼び出すことができます。でも親クラスのPrivatefinanceのクラスでそのクラスがあるから使えることです。

Accountクラスでinput関数を作成しました。Privatefinanceのクラスにもあるクラスですが、input関数を再作成しました。でもAccountクラスのinput関数とPrivatefinanceクラスのinput関数はパラメータタイプが違うので同じ関数ではないです。

でも同じパラメータなら作成することができないかというとJavaには再定義(override)という機能がありまして、可能です。再定義(override)のことは説明が長いので別途の投稿で説明します。


後、Accountクラスのinput関数をみれば中でsuper.input(money)を作成したことを確認できます。

ここでthisとsuperの差異を分かりますが、thisは現在のクラス(Accountクラス)の領域を参照すること、superは親クラス(Privatefinanceクラス)の領域を話すことです。

つまり、super.inputはPrivatefinanceクラスのinput関数を呼び出すことです。

Copy!
 [Source view] 
// 親クラス(SuperProgram)
class SuperProgram {
// 関数
public void print() {
// コンソール出力
System.out.println("SuperProgram print");
}
}
// クラス、SuperProgramクラスを継承する。
public class Program extends SuperProgram{
// 関数
public void print() {
// コンソール出力
System.out.println("Program print");
}
// 関数
public void run() {
// 親クラスのprint関数を呼び出す。
super.print();
// 現クラスのprint関数を呼び出す。
this.print();
}
// main関数
public static void main(String... args) {
// Programインスタンスを生成
Program p = new Program();
// run関数呼び出す。
p.run();
}
}


上の例をみればProgramクラスのrun関数を呼び出したらthisとsuperの差異が明確に見えます。


それなら継承の長所はなにか?

ソースの可読性がよくなります。ソースの可読性はどの程にソースが簡単に読めるかということですが、ソースのステップが少なるから可読性がよくなることです。

管理の容易性もよいです。もし、コピペでクラスを作成したと思えば最初のクラスでバグがある場合にコピペしたすべてのクラスを修正しなければならないです。そうなら作業の漏れが発生する可能性もあるし、そのことで管理性が大変になることです。

でも、継承機能を利用すれば?そうです。最初の親クラスだけ修正すればすべてか反映されます。

そして最初にクラスの継承はOOP特性の一つなので、そのことによって設計パターンと開発方法が多いし、Javaというプログラム言語をすごく効率的に利用することができます。


ここまでクラスの継承とthis、superキーワードの使い方に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿