ここでは、オブジェクト指向の三大要素のうちのポリモーフィズム
について説明します
ポリモーフィズム
ポリモーフィズム
とは、多態性(多様な動作をする)という意味のことです
Javaでは、スーパークラスの型を持った参照変数にサブクラスのインスタンスを代入できたり、同じ名前のメソッドを呼び出してもインスタンスの種類によって異なる処理が実行されるような性質を持っています
たとえば応用編 1日目で説明したShapeクラス、Rectangleクラスを再掲します
class Shape {
int color;
int dot;
Shape(){}
Shape(int color,int dot){
this.color=color;
this.dot=dot;
}
double calcArea(){
return 0;
}
}
class Rectangle extends Shape{
double width;
double height;
Rectangle(){}
Rectangle(int color,int dot,double width,double height){
super(color,dot);
this.width=width;
this.height=height;
}
double calcArea(){
return width * height;
}
double calcAround(){
return (width+height)*2;
}
}
public class Sample {
public static void main(String[] args) {
Shape sh = new Shape(0x000001,1);
double area = sh.calcArea();
System.out.println("shape area="+area);
Rectangle rect = new Rectangle(0xffffff,1,10,20);
area = rect.calcArea();
System.out.println("rectangle area="+area);
double round = rect.calcAround();
System.out.println("rectangle round="+round);
}
}
area = sh.calcArea();
area = rect.calcArea();
Rectangleクラス内のcalcAreaメソッドはオーバーライドしたメソッドだと説明していました
これも、ポリモーフィズムの性質である「同じ名前のメソッドを呼び出してもインスタンスの種類によって異なる処理が実行される」ということに依るものです
もう一つの性質として「スーパークラスの型の参照変数に、サブクラスのインスタンスを代入できる」というものがあります
以下にサンプルプログラムを示します

この図のようにRectangleクラスとTriangleクラスをShapeクラスから継承し、ともにcalcAreaメソッドをオーバーライドしています
class Shape {
int color;
int dot;
Shape(){}
Shape(int color,int dot){
this.color=color;
this.dot=dot;
}
double calcArea(){
return 0;
}
}
class Rectangle extends Shape{
double width;
double height;
Rectangle(){}
Rectangle(int color,int dot,double width,double height){
super(color,dot);
this.width=width;
this.height=height;
}
double calcArea(){
return width * height;
}
double calcAround(){
return (width+height)*2;
}
}
class Triangle extends Shape{
double bottom;
double height;
Triangle(){}
Triangle(int color,int dot,double bottom,double height){
super(color,dot);
this.bottom=bottom;
this.height=height;
}
double calcArea(){
return (bottom * height)/2;
}
}
public class Sample {
public static void main(String[] args) {
Shape rect = new Rectangle(0xff0000,1,10,20);
Shape triangle = new Triangle(0x00ff00,2,10,20);
double area = rect.calcArea();
System.out.println("Rectangle Area = " + area);
area = triangle.calcArea();
System.out.println("Triangle Area = " + area);
}
}
Shape rect = new Rectangle(0xff0000,1,10,20);
Shape triangle = new Triangle(0x00ff00,2,10,20);
まず、この行に注目してください
Shape型の変数を定義し、その変数にはRectangleクラス、Triangleクラスのインスタンスを代入しています
スーパークラスの型を持った参照変数には、サブクラスのインスタンスを代入できるため、このような代入でもエラーは出ません
double area = rect.calcArea();
area = triangle.calcArea();
そして、calcAreaメソッドを呼び出していますが、オーバーライドしたRectangleクラス、Triangleクラスのメソッドが呼び出されます
しかし、以下のように記述するとエラーが発生します
Rectangle sh = new Shape(0xffff00,1);
スーパークラスのインスタンスをサブクラスの変数には代入することはできないからです
double round = triangle.calcAround();
calcAroundメソッドはRectangleクラスで定義されているため、他のインスタンスからは参照することはできません
また、配列を利用して以下のように記述することもできます
public class Sample {
public static void main(String[] args) {
Shape[] ary = new Shape[3];
ary[0] = new Shape(0xfff000,3);
ary[1] = new Rectangle(0xff0000,1,10,20);
ary[2] = new Triangle(0x00ff00,2,10,20);
for(int i=0; i<ary.length;i++){
double area = ary[i].calcArea();
if(ary[i] instanceof Rectangle){
System.out.print("Rectangleクラス");
}
else if(ary[i] instanceof Triangle){
System.out.print("Triangleクラス");
}
else if(ary[i] instanceof Shape){
System.out.print("Shapeクラス");
}
System.out.println("(" + i + "):" + area);
}
}
}
実行結果
Shapeクラス(0):0.0
Rectangleクラス(1):200.0
Triangleクラス(2):100.0
Shape[] ary = new Shape[3];
ary[0] = new Shape(0xfff000,3);
ary[1] = new Rectangle(0xff0000,1,10,20);
ary[2] = new Triangle(0x00ff00,2,10,20);
Shape型の配列を用意し、Shape、Rectangle、Triangleのインスタンスをそれぞれ代入しています
double area = ary[i].calcArea();
それぞれの配列の要素が指すインスタンスのメソッドが呼び出されます
if(ary[i] instanceof Rectangle){
インスタンスがどのクラスのものか判断するため、instanceof
演算子を使用しています
インスタンスがRectangleクラスのものであればTrue
そうでなければFalse
が返ってきます
関数の引数に渡すときも以下のように記述することができます
public class Sample {
public static double calcArea(Shape shp){
if(shp instanceof Rectangle){
System.out.print("Rectangleクラス");
}
else if(shp instanceof Triangle){
System.out.print("Triangleクラス");
}
else if(shp instanceof Shape){
System.out.print("Shapeクラス");
}
return shp.calcArea();
}
public static void main(String[] args) {
Shape[] ary = new Shape[3];
ary[0] = new Shape(0xfff000,3);
ary[1] = new Rectangle(0xff0000,1,10,20);
ary[2] = new Triangle(0x00ff00,2,10,20);
for(int i=0; i<ary.length;i++){
double area = calcArea(ary[i]);
System.out.println("(" + i + "):" + area);
}
}
}
public static double calcArea(Shape shp){
Sampleクラス内でShape型の変数を引数としたメソッドを作成しました
return shp.calcArea();
それぞれのインスタンスのメソッドが呼び出されます
for(int i=0; i<ary.length;i++){
double area = calcArea(ary[i]);
System.out.println("(" + i + "):" + area);
}
calcAreaメソッドの仮引数は、ary[i]と同じインスタンスを参照することになります
【演習問題】
- 前述したソースを実装してみましょう
- Rectangleクラスに存在するcalcAroundメソッドをShapeクラスに移動し、すべてのサブクラスでオーバーライドしてみましょう
calcAroundは図形の外周の長さを求める機能を持っているものとします - Shapeクラスから継承したCircleクラスを作成し、calcArea、calcAroundメソッドを呼び出してみましょう
コメント