Eiffel (アイフェル、エッフェル)とは
Eiffelは、堅牢なソフトウェア開発を目的とした
オブジェクト指向プログラミング言語です。
1985年にバートランド・メイヤーによって考案され、
Pascalに似た文法を持ちます。静的型付けを強く指向し、動的なメモリ管理(ガベージコレクション)を備えています。
特徴
Eiffelは、かつてSmalltalkと並び、オブジェクト指向の教科書的な言語として扱われていました。多重継承、ガベージコレクションといった高度な機能に加え、設計者によって
ライブラリの
メンテナンスが重視され、契約による設計(Design By Contract)の概念が強く打ち出されています。
Javaのようなオブジェクト指向言語と比較すると普及度は低いですが、アサーションなど、
Javaが後追いする部分もあります。インタフェースによる継承やガベージコレクションなど共通点も多いです。EiffelはC/
C++のようにネイティブコードを直接生成するのではなく、
C言語や
Javaのコードを生成する点も特徴的です。
言語名の由来は、
エッフェル塔ではなく、その設計者である
ギュスターヴ・エッフェルです。
基本構成
Eiffelの
ソースコードは、以下のように記述されます。
eiffel
class MY_CLASS
feature
my_attribute: INTEGER
make is
do
my_attribute := 10
end
my_method is
do
print ("Hello, Eiffel!")
end
end
Eiffelでは、「クラスとはオブジェクトの生成機である」という考え方が徹底されており、クラス変数やクラスメソッドといった、クラスとオブジェクトの概念を混同するような機能は存在しません。これは「クラスもオブジェクトの一種である」とするSmalltalkとは対照的です。
また、「クラス」に対する考え方も独特です。
Javaのように、ソースファイルをコンパイルして「クラスファイル」を作成するのではなく、Eiffelでは「クラス」とは「
ソースコードそのもの」であると考えます。コンパイルで生成されるファイルは、「クラス(
ソースコード)によって作られた
インスタンス」とみなされます。そのため、Eiffelではメインルーチンに相当する処理をコンストラクタで行います。
コンストラクタは`creation`下で宣言されたメンバ関数を使用します。このメンバ関数の名前は任意ですが、慣例的に`make`が用いられます。クラスの
インスタンスを作成する際は、以下のように記述します。
eiffel
create my_object.make
(コンストラクタが定義されているクラスは、
インスタンス作成時に必ずコンストラクタを呼び出す必要があります。)
クラスの継承を省略した場合、自動的に`ANY`クラスの継承クラスとして扱われます。入出力などのグローバルな機能は`ANY`クラス内で定義されており、各Eiffelクラスでは、これらの機能の実装を考慮する必要はありません。
なお、Eiffelではクラス名を全て大文字で記述するという命名規則があります。
renameとredefine
Eiffelの大きな特徴の一つに、継承における詳細な設定が可能な点が挙げられます。例えば、以下のようなコードを考えてみましょう。
eiffel
class A
feature
method1 is
do
print ("A's method1")
end
end
class B
inherit
A
feature
method1 is
do
print ("B's method1")
end
end
`A`クラスを継承した`B`クラスに、同じ名前のメソッドがある場合、`b.method1`を実行すると、誰もが`B`で定義されたメソッドが実行されると期待するでしょう。しかし、`a.method1`で実行されるメソッドは`A`と`B`のどちらのものでしょうか?
この処理は言語によって異なり、例えば
C++では`A`で定義されたメソッドが実行され、
Javaでは`B`のメソッドが実行されます。このように、言語によって動作が異なるため、開発者の勘違いなどによる混乱を招く可能性があります。
Eiffelでは、そもそも上記のような例ではコンパイルエラーになります。Eiffelのメソッドはデフォルトでは継承不可であるためです。しかし、これでは制約が強すぎるため、同名のメソッドを定義する必要がある場合は、継承クラスの作成者がどちらを実行するかを指定できます。
具体的には、`rename`と`redefine`という機能があります。
`A`で定義されたメソッドを実行したい場合は、以下のように記述します。
eiffel
class B
inherit
A
rename
method1 as method2
feature
method1 is
do
print ("B's method1")
end
end
この場合、クラス`B`では、`A`のメソッドを`method2`という名前に変更し、名前の衝突を防いでいます。`B`の
インスタンスが、`A`の
インスタンスとして扱われた場合は、`A`の`method1`が実行され、`B`の
インスタンスとして扱われた場合は、`B`の`method1`が実行されます。`B`の
インスタンスとして扱われた状態で、`A`の`method1`を実行するには、上記の例では`b.method2`と記述します。
これらの名前変更の効果は、継承元と継承先だけでなく、多重継承時のメソッド同士の衝突にも使用できます。
逆に、`A`の
インスタンスとして扱われる場合でも、`B`の`method1`を実行したい場合は、以下のように記述します。
eiffel
class B
inherit
A
redefine
method1
feature
method1 is
do
print ("B's method1")
end
end
この場合、クラス`B`はクラス`A`の`method1`を継承時に破棄し、クラス`B`で再定義することを意味します。クラス`B`の
インスタンスは、`A`、`B`どちらで扱われても、`method1`はクラス`B`のものが実行され、クラス`A`の`method1`はもはや実行されることはありません。
再定義を宣言された`method1`は、必ずクラス`B`で再定義する必要があり、再定義されていない場合はコンパイルエラーとなります。
このように、Eiffelではクラスの継承時に、継承クラスの作成者がメソッドの扱いを自由に設定できます。
コンストラクタの非継承
Eiffelの特徴の一つとして、多重継承をサポートしていることが挙げられます。
多重継承時に問題となるものの一つに、継承した複数のクラスの両方(あるいはそれ以上)にコンストラクタが定義されている場合、どちらのコンストラクタが実行されるか?という問題があります。Eiffelの他に多重定義をサポートしている
C++や
Pythonでは、継承元のコンストラクタの実行順序に複雑な規則が導入されています。
Eiffelでは、コンストラクタは継承されないことになっています。このため、コンストラクタの実行手順といったものは、原理的に存在しません。どうしても継承元のコンストラクタを使用する必要がある場合は、継承時に`rename`でコンストラクタとして宣言されているメソッドを改名し、改名したメソッドをコンストラクタ内で実行することで実装できます。
脚注
外部リンク
Eiffel.org
Eiffel: 一歩進んだ入門 - ウェイバックマシン(1999年10月8日アーカイブ分)
*
SmartEiffel The GNU Eiffel Compiler - SmartEiffel (GNUプロジェクトによるフリーのEiffel
コンパイラ)配布ページ