円とか正方形とかがあってその面積の合計を求めるとします。
そのとき、基本クラスとして平面図形というものを考えます。
しかし、平面図形の面積を計算するプログラムを書け、
といわれても困ってしまいますね。
こういう具体的でないクラスを抽象クラスといいます。
そして、ここから円クラスとか正方形クラスとかが派生して、
計算ができるようになります。
つまり抽象クラスは派生されて初めて仕事をします。
コードを示しましょう。
area.js
-------------------------------------------------------------------
var f : CFigure[] = new CFigure[3];
f[0] = new CCircle(1); //半径1の円
f[1] = new CSquare(2); //辺の長さ2の正方形
f[2] = new CRectangle(2, 3); //辺の長さ2と3の長方形
//面積を足し合わせる
var area = 0;
for(var i = 0; i < 3; i++)
area += f[i].getArea();
print(area);
//平面図形クラス(抽象クラス)
abstract class CFigure {
abstract function getArea() : double;
}
//円クラス
class CCircle extends CFigure {
private var r : double;
function CCircle() { r = 0; }
function CCircle(x : double) { r = x; }
function getArea() { //抽象クラスのメソッドの実装
return Math.PI * r * r;
}
}
//正方形クラス
class CSquare extends CFigure {
...
}
//長方形クラス
class CRectangle extends CFigure {
...
}
抽象クラスは、修飾子
abstractをつけます。
そして、実装がないメンバにも
abstractをつけます。
当然ですが、抽象クラスはインスタンスを生成できません。
abstract class CFigure {
abstract function getArea() : double;
}
//error JS1214: 抽象型クラスのインスタンスを構築することはできません
var a = new CFigure();
インターフェイスとは、その名の通り、外面(そとづら)だけ提供するものです。
要は、メソッドの引数と返り値の型だけ与えています。
具体的には、
IComparerインターフェイスを見てもらうと
イメージとしては分かりやすいと思うのですが、
より理解するために、このインターフェイスに似たものを作ってみましょう。
次の例は現在のディレクトリのファイルを更新日の順に表示するプログラムです。
ls.js
-------------------------------------------------------------------
import System;
import System.IO;
import System.Collections;
//比較のためのインターフェイス
interface IComp {
function less(a : Object, b : Object) : boolean;
}
function Sort(ary : ArrayList, icmp : IComp) {
for(var i = 0; i < ary.Count - 1; i++) {
var imin : int = i;
for(var j = i + 1; j < ary.Count; j++) {
if(icmp.less(ary[imin], ary[j]))
imin = j;
}
var tmp = ary[imin];
ary[imin] = ary[i];
ary[i] = tmp;
}
}
//現在のディレクトリのファイルの配列を得て
var a = new ArrayList();
var d = new DirectoryInfo(".");
var fs = d.GetFiles();
for(var i = 0; i < fs.length; i++)
a.Add(fs[i]);
//更新日順に並べ替えて
Sort(a, new DateComp());
//表示する
for(i = 0; i < a.Count; i++)
Console.WriteLine("{0,-20:S} {1:S}",
a[i].Name, a[i].LastWriteTime.ToString());
//ICompの実装
class DateComp implements IComp {
function less(a : Object, b : Object) : boolean {
return FileInfo(a).LastWriteTime > FileInfo(b).LastWriteTime;
}
}
インターフェイスは次のように定義します。
interface IComp {
function less(a : Object, b : Object) : boolean;
}
そして次のようにファイルの更新日時で比較するように実装します。
class DateComp implements IComp {
function less(a : Object, b : Object) : boolean {
return FileInfo(a).LastWriteTime > FileInfo(b).LastWriteTime;
}
}
extendsではなく
implementsとすることに注意。
ソートは2つのオブジェクトa,bについて
aがbより小さいかどうかさえわかればいいので、
このインターフェイスの実装があれば並べ替えられるわけです。
抽象クラスとインターフェイスは、派生クラスで実装するという点で似ていますが、
次のような違いがあります。
全体に、その意味を重視して簡明にした感じでしょうか。
抽象クラスは次のように何度も継承できますが、
インターフェイスは一度しか継承できません。
abstract class ClassA {
abstract function methodA();
}
//抽象クラスを継承
class ClassB extends ClassA {
function methodA() { print("A"); }
}
//さらに継承
class ClassC extends ClassB {
function methodB() { print("B"); }
}
var c : ClassC = new ClassC();
c.methodA(); //A
c.methodB(); //B
抽象クラスは多重継承できませんが、インターフェイスは可能です。
interface IntA {
function methodA();
}
interface IntB {
function methodB();
}
//2つのインターフェイスを継承
class ClassA implements IntA, IntB {
function methodA() { print("A"); }
function methodB() { print("B"); }
}
var a : ClassA = new ClassA();
a.methodA(); //A
a.methodB(); //B
あと、抽象クラスは実装を含むことができますが、
インターフェイスは実装が一切あってはいけません。