Quick introductionの邦訳その2です。
ミックスイン継承
javaと同じように、Ceylon にはクラスとインターフェースがあります。クラスは単一のスーパークラスと任意の数のインターフェースを継承できます。インターフェースは任意の数の他のインターフェースを継承でき、Object 以外の、クラスを拡張することはできません。Java とは異なり、インターフェースは具象メンバを定義することができます。つまり、Ceylon はミックスイン継承(mixin inheritance)と呼ぶ、制限された多重継承をサポートするということになります。
interface Sized { shared formal Integer size; shared Boolean empty { return size==0; } } interface Printable { shared void printIt() { print(this); } } object empty satisfies Sized & Printable { shared actual Integer size { return 0; } }
Ceylon において、インターフェースとクラスの違いは、インターフェースが状態を持たないことです。つまり、インターフェースは他のオブジェクトの参照を直接持ことはありません。これは、初期化ロジックを持たず、直接インスタンス化されることがないということになります。Ceylon はどんな種類のスーパータイプの「線形化」の実行する必要性を避けています。
多様属性(Polymorphic attributes)
Ceylon には、伝統的な感覚で言うフィールドを持っていません。その代わり属性は多様的で、オブジェクト指向言語におけるメソッソのように、サブクラスにて派生されます。
属性が単純な値である場合は
String name = firstName + " " + lastName;
ゲッターの場合は
String name { return firstName + " " + lastName; }
また、ゲッターとセッターのペアの場合は
String name { return fullName; } assign name { fullName := name; }
のようになります。
Ceylon ではクラスの状態は、常にクラスのクライアントから完全に抽象化されるため、つまらないゲッターとセッターを書く必要はありません。
型安全な null と安全な型縮小
Ceylon では NullPointerException やそれに類するものはありません。Ceylon は null と成りうる値やメソッドが null を戻す場合には明示的に宣言することを要求します。例えば、name が null となる場合があれば、次のように宣言しなければなりません。
String? name = ...
この記述は、実際には以下の省略系です。
String|Nothing name = ...
String? 型の属性は、実際にはStringのインスタンスか、null値(Nothing クラスのインスタンスのみ)を要求します。Ceylon は、特別な if (exists ...) 構成を使い、null 値をチェックすることなしには String? 型の値を使うことができません。、
void hello(String? name) { if (exists name) { print("Hello, " name "!"); } else { print("Hello, world!"); } }
同様に、Ceylon には ClassCastException もありません。その替りに if (is ...) と case (is ...) により1手順で型の確認と型の縮小化が行えます。実際、前述のコードは、以下の記述のショートカットでもあります。
void hello(String? name) { if (is String name) { print("Hello, " name "!"); } else { print("Hello, world!"); } }
列挙サブタイプ(Enumerated subtypes)
オブジェクト指向プログラミングにおいて、型の全てのサブタイプを扱うような長い switch ステートメントを書くことは、たいてい良くない慣習とされています。これは拡張性に欠けてしまうのです。新しいサブタイプを加えると、たちまち switch ステートメントが壊れてしまいます。そのためオブジェクト指向のコードでは、しばしばサブクラスで派生できるように、親クラスに抽象メソッドを使うようにリファクタリングされます。
しかしながら、この種のリファクタリングが適さない種類の問題があります。大抵のオブジェクト指向言語では、この種の問題は visitor パターンを使うことで解決しています。不幸なことに、visitor クラスは switch より冗長であり、クラスの拡張性も犠牲になっています。一方で visitor パターンには大きな優位性があるのも事実であり、新しいサブタイプを加え、visitor に反映を忘れた場合にはコンパイルエラーとして検知できます。
Ceylon はこの2つのジレンマを解消します。親型を定義する時に、サブ型のリストを列挙して指定することができます。
abstract class Node() of Leaf | Branch {}
そして我々は、全ての列挙サブ型を扱う switch文 を書くことができます。
Node node = ... ; switch (node) case (is Leaf) { ... } case (is Branch) { .... }
ここで、Node のサブ型を加えた場合、我々は Node の定義条件を追加しなければならず、新しいサブ型を処理しない全ての switch 文についてエラーを報告します。
blog1.mammb.com
に続く...