クラスの継承とは、クラスが保持しているメンバやメソッドの内容を他のクラスで継承することを指します。
継承元のクラスのことを「スーパークラス」「親クラス」などと呼び、継承したクラスのことを「サブクラス」「子クラス」などと呼びます。親クラスを継承するためにはextendsを指定します。
class 親クラス名 { 親クラスのメンバ変数; 親クラスのメソッド; }
class 子クラス名 extends 親クラス名 { super.親クラスのメンバ変数; super.親クラスのメソッド; }※「super.」は省略可。 但し、子クラスに親クラスと同じ名前のメンバ変数やメソッドがある場合は、明示的に「super.」を付加しないと自クラス(子クラス)のものが使用されます。
他のクラスの要素を利用できる方法として、「継承」の他に「コンポジション」(集約)があります。
package JSample; class Animal { public void meow(){ System.out.println("ニャー"); } } class Name { public void printName(){ System.out.println("タマネギ"); } } class Cat extends Animal { public Name catName = new Name(); } public class JSample11_1 { public static void main(String[] args) { Cat neko = new Cat(); neko.meow(); neko.catName.printName(); } }実行結果
「親クラス」と「子クラス」のコンストラクタは次の規則で呼び出されます。
package JSample; class Oya { public String parentStr = "親クラスのメンバ変数が参照されました。"; public Oya() { System.out.println("親クラスのコンストラクタ(引数なし)が呼ばれました。"); } public void oyaMethod() { System.out.println("親クラスのメソッドが呼ばれました。"); } } class Kodomo extends Oya { // 親クラスを継承。 public String childStr = "子クラスのメンバ変数が参照されました。"; public Kodomo() { System.out.println("自クラスのコンストラクタ(引数なし)が呼ばれました。"); } public void childMethod() { System.out.println("子クラスのメソッドが呼ばれました。"); } } public class JSample11_2 { public static void main(String[] args) { Kodomo child = new Kodomo(); // 子クラスのインスタンスを生成。 System.out.println(child.parentStr); // 親クラスのメンバ変数を参照。 System.out.println(child.childStr); // 子クラスのメンバ変数を参照。 child.oyaMethod(); // 親クラスのメソッドの呼び出し。 child.childMethod(); // 子クラスのメソッドの呼び出し。 } }実行結果
thisとは、呼び出されたコンストラクタやメソッドのオブジェクトを参照するために使われる予約語である。
thisを使ってメソッドの引数になっているローカル変数と、メンバ変数を区別することができます。
例:[JSample11_3.java]
class Animal { private String name = "タマネギ"; public void printName(String name){ System.out.println("ローカル変数 = " + name); System.out.println("メンバ変数 = " + this.name); } } public class JSample11_3 { public static void main(String[] args) { String name = "ナス"; Animal neko = new Animal(); neko.printName(name); } }実行結果
superとは、Javaにおけるサブクラスでオーバーライド(親クラスのメソッドを子クラスで継承)された変数やインスタンスを参照する場合に使用されます。
つまり、子クラスのインスタンス(new [クラス名()]で作られたクラスの実体のこと)から、親クラスのインスタンスのメンバにアクセスして、値を参照する必要があるときにsuperが使用されます。
例:[JSample11_4.java]
package JSample; class SuperClass { String str = "SuperClass"; public String getStr() { return str; } } class SubClass extends SuperClass { String str = "SubClass"; public String getStr() { return str; } public void print() { System.out.println("str = " + str); System.out.println("getStr() = " + getStr()); System.out.println("super.str = " + super.str); System.out.println("super.getStr() = " + super.getStr()); } } public class JSample11_4 { public static void main(String[] args) { SubClass sc = new SubClass(); sc.print(); } }実行結果
クラスを継承した時に元になっているスーパークラスで定義されているメソッドを継承したサブクラスにて同じメソッド名(と同じ引数)で書き換えることが出来ます。つまり上書きするということです。これをメソッドのオーバーライドと言います。
具体的な例で考えてみます。スーパークラスとしてクラスSuperClassを用意し、クラスSuperClassを継承したクラスSubClass1、クラスSubClass2があったとします。スーパークラスであるクラスAには「show」というメソッドが定義されています。ここでクラスB1で「show」というメソッドをオーバーライドしてみます。
例:[JSample11_5.java]
package JSample; class SuperClass { public void show() { System.out.println("SuperClassのprint メソッド!"); } } class SubClass1 extends SuperClass { public void show() { System.out.println("SubClassのprint メソッド!"); } } class SubClass2 extends SuperClass { } class JSample11_5 { public static void main(String args[]) { B1 subc1 = new SubClass1(); subc1.show(); B2 subc2 = new SubClass2(); subc1.show(); } }実行結果
メソッドを引数を付けて呼び出す時、引数に記述する値のデータ型はメソッドで決められたものしか指定できません。その為、同じような機能を持つメソッドであっても引数のデータ型が異なれば別々のメソッドを用意する必要があります。
Javaでは引数のデータ型や引数の数が完全に一致していなければ異なるメソッドに同じメソッド名を付けることが出来ます。
例:[JSample11_6.java]
package JSample; class JSample11_6 { public static void main(String args[]) { System.out.println(plus(10, 7)); System.out.println(plus(3.2, 4)); System.out.println(plus(7, 1.223)); System.out.println(plus(5.08, 2.4)); } private static int plus(int n1, int n2) { System.out.println("int + int"); return n1 + n2; } private static double plus(int n1, double d1) { System.out.println("int + double"); return n1 + d1; } private static double plus(double d1, int n1) { System.out.println("double + int"); return n1 + d1; } private static double plus(double d1, double d2) { System.out.println("double + double"); return d1 + d2; } }実行結果
final修飾子はクラス、メソッド、変数の不変性を示して、対象の予期せぬ変更を防ぐために用います。
一度しか値を代入することができない変数を定数になったため、再代入が禁止になります。
class MyClass { void myMethod() { final int a = 0; a = 1; } }エラーメッセージ:
final class MyClass { } class MySubClass extends MyClass { }エラーメッセージ:
class MyClass { final public void myMethod() { } } class MySubClass extends MyClass { public void myMethod() { } }エラーメッセージ:
package JSample; class Circle { final double PI = 3.14159;//final変数PIの宣言、初期化 final double getAreaOfCircle(double radius) {//finalメソッド宣言 return PI * radius * radius;//円の面積 } } public class JSample11_7 { public static void main(String[] args) { Circle circle = new Circle();//Circleクラスのオブジェクト作成 double area = circle.getAreaOfCircle(2.0);//半径2.0の面積 System.out.println("area = " + area); } }実行結果
問題[JEx11_1]
従業員の管理者を表すManagerクラスを、Employeeクラスを継承してプログラムを完成しなさい
[Employee.java]
public class Employee { //処理 }[Manager.java]
public class Manager { //処理 }[JEx11_1.java]
public class JEx11_1 { public static void main(String[] args) { Employee taro = new Employee(); sato.name = "佐藤"; suzuki.name = "鈴木"; Manager yamada = new Manager(); yamada.name = "山田"; //satoの持つoperationメソッドを呼び出します。 //suzukiの持つoperationメソッドを呼び出します。 //yamadaの持つoperationメソッドを呼び出します。 //yamadaの持つmanagementメソッドを呼び出します。 } }実行結果
問題[JEx11_2]
RPGに登場する魔法戦士を表すMagicFighterクラスを、Fighterクラスを継承して作成してプログラムを完成しなさい
[Fighter.java]
public class Fighter { //処理 public Fighter() { //処理 } public void attack() { //処理 } }[MagicFighter.java]
public class MagicFighter extends Fighter { //処理 public MagicFighter() { //処理 } public void attack() { //処理 } }[JEx11_2.java]
public class JEx11_2 { public static void main(String[] args) { Fighter fighter = new Fighter(); fighter.attack(); MagicFighter magicFighter = new MagicFighter(); magicFighter.attack(); } }実行結果