パラダイム | オブジェクト指向プログラミング |
---|---|
設計者 | バートランド・メイヤー |
最新リリース | EiffelStudio 22.12[1]/ 2022年12月22日 |
型付け | 強い静的型付け |
主な処理系 | EiffelStudio, LibertyEiffel, SmartEiffel, Visual Eiffel, Gobo Eiffel, "The Eiffel Compiler" tecomp |
影響を受けた言語 | Ada、Simula |
影響を与えた言語 | Sather、Ruby、Java、C#、D |
Eiffel(アイフェル、エッフェル)は頑健なソフトウェアの生産に注力したオブジェクト指向プログラミング言語である。
1985年にバートランド・メイヤー(Bertrand Meyer)によって考案された。その文法はPascalを連想させるものである。Eiffelは静的な型指定を強く指向しており、かつ動的なメモリ管理(一般的にガベージコレクションにより実装される)を備えている。
少し前、オブジェクト指向の教科書的言語といえば、SmalltalkかEiffelか、という状況で、手続き型言語でのPascalのような存在であった。多重継承、ガベージコレクションといった特徴があるが、設計者によってライブラリのメンテナンスが重視されており、契約による設計(Design By Contract)の概念が全面に打ち出されている。同じくオブジェクト指向を取り入れた言語であるJavaほどは普及していない。Assertionなど、Javaが追いかけているところもあるが、インタフェースによる継承、GCあり、とかぶるところが多い。
また、C/C++のようにネイティブコードを直接生成するのではなく、C言語やJavaのコードを生成する、という特徴ももっている。
言語名の由来は、エッフェル塔ではなく、その設計者ギュスターヴ・エッフェルである。
Eiffel のソースは以下のように記述する。
-- コメント
class クラス名
inherit
継承元のクラス(継承しない場合は省略可)
creation
コンストラクタの宣言(コンストラクタが必要ない場合は省略可)
feature{アクセス権限}
メンバ変数、メンバ関数の記述
end
Eiffel は「クラスとはオブジェクトの生成機である」という考え方が徹底しており、このため両者の概念を混同するようなクラス変数やクラスメソッドの機能は存在しない。このことは「クラスもオブジェクトの一種である」と考える Smalltalk とは対照的である。
また「クラス」に対する考え方も独特で、例えば Java ではソースファイルをコンパイルすると「クラスファイル」というファイルを作るのを見てわかるように、一般的には「ソースコード」は「クラスの設計図」という概念であるのに対し、Eiffel では「クラス」とは「ソースコードそのものである」という考え方である。コンパイルして生成されるファイルは「クラス(ソースコード)によって作られたインスタンス」という考え方であり、このため Eiffel ではメインルーチンに相当する処理をコンストラクタで行う。
コンストラクタはcreation
下で宣言されたメンバ関数が使用される、このメンバ関数はどんな名称でも構わないが慣例的にmake
とする場合が多い。作成したクラスを使用する場合は
x :(クラス名)
!!x.(コンストラクタ名)
でインスタンスを作成する(コンストラクタが指定されているクラスは上記の構文中で必ずコンストラクタを呼び出さなければならない)。
クラスの継承を省略した場合は ANY というクラスの継承クラスとして扱われる。入出力などのグローバルな機能は ANY クラス内で定義されており、各 Eiffel のクラスではこれらの機能の実装に関して考慮をする必要性がないようになっている。
なお、Eiffelはクラス名は全て大文字で書かなければならないという命名規則がある。
Eiffel の特徴の一つとして、継承における細かい指定が可能ということが挙げられる。例えば
class A
feature
method1 is
io.put_string("Hello from A")
io.new_line
end
end
class B
inherit
A
feature
method1 is
io.put_string("Hello from B")
io.new_line
end
end
というコードがあるとする(ioは標準入出力を扱うインスタンスで、ANYクラスのメンバである)。これは A というクラスを継承した B というクラスに同じ名前のメソッドがある場合である。このとき
class C
creation
make
feature
a : A
b : B
make is
do
!!b
a := b
b.method1
a.method1
end
end
というコードを実行したとき。b.method1 はおそらく B で定義されたメソッドが動くと誰もが期待するだろうが、a.method1 で実行されるメソッドは A と B どちらで定義されたものだろうか?この場合の処理は言語によって異なっており例えばC++の場合は A で定義されたメソッドが実行され、Javaの場合は B のメソッドが実行される。すなわち言語によって動作が違うため作成者の勘違いなどによって混乱を招くおそれがある。
Eiffelでは実のところ、どちらが実行されるか以前に上記のような例ではコンパイルすることができないことになっている。すなわち Eiffelのメソッドは全てデフォルトでは継承不可である。しかしこれでは、あまりに制約が強すぎるため、同名のメソッドを定義する必要性があるときは、継承クラスの作成者がどちらを実行するかを指定することが出来る。
具体的には rename と redefine という機能があり、上記の例では A で定義されたメソッドを実行したい場合は
class B
inherit
A
rename
method1 as method2
end
とする。この場合クラス B では A のメソッドを method2 という名前に改名させて、名前の衝突を防いでいる。B のインスタンスでも、クラス A のインスタンスとして扱われた場合(上記の例のような場合)は A の method1 が実行され、クラス B のインスタンスとして扱われた場合は B のmethod1 が実行される。B のインスタンスとして扱われた状態で A の method1 を実行するには上記の例では b.method2 とすればよい。これらの改名による効果は継承先と継承元の他、多重継承時のメソッド同士の衝突にも使用できる。
逆に A のインスタンスとして扱われる場合でも B の method1 を実行したい場合は以下のようにする。
class B
inherit
A
redefine
method1
end
この場合、クラス B はクラス A の method1 を継承時に破棄しクラス B で再定義することを示している。クラス B のインスタンスは A、B どちらで扱われても method1 はクラス B のものが実行され、クラス A の method1 はもはや実行されることは無い。なお再定義を宣言された method1 は必ずクラス B で再定義しなければならず、再定義していない場合はコンパイルエラーとなる。
このように、Eiffel ではクラスの継承時に継承クラスの作成者がメソッドの扱いを自由に設定することができる。
Eiffel の特徴の一つとして多重継承をサポートしていることが挙げられる。
多重継承時に問題となるものの一つとして、継承した二つの(あるいはそれ以上)のクラスのどちらにもコンストラクタが定義されている場合、二つのクラスのどちらのコンストラクタが実行されるか?というものがある。Eiffel の他に多重定義をサポートしている C++や Python では継承元のコンストラクタの実行順に複雑な規則が導入されている。
Eiffelではコンストラクタは継承されないことになっている。このためコンストラクタの実行手順といったものは原理的に存在しない。どうしても継承元のコンストラクタを使用する必要がある場合は、継承時に rename でコンストラクタとして宣言されているメソッドを改名し、改名したメソッドをコンストラクタ内で実行することで実装できる。