Ceylonのクラスについて

今回はCeylonのクラスについて少しメモ。JavaC++に慣れているとCeylonのクラス定義は少し独特です。


doc "Person class has the parameters that are person's
first name, last name and age."
class Person(String firstName, String lastName, Integer age) {

shared String whatIsYourName() {
return "My name is " + firstName + " " + lastName + ".";
}

shared String howOldAreYou() {
return "I'm " + age.string + " years old.";
}

shared String aboutYourself() {
return whatIsYourName() + "\n" +howOldAreYou();
}

shared String firstNameIs() {
return firstName;
}

shared String lastNameIs() {
return lastName;
}

shared Integer ageIs() {
return age;
}
}

見てわかるようにCeylonではpublic、protected、privateの区別がありません。代わりにsharedというアノテーションでクラス、メソッドの可視性を定義します。このクラスはclass Personと定義しているためパッケージプライベートで、各メソッドはパッケージ内のどのクラスからでもアクセス可能となります。

次にclass宣言部分のPersonの右に(...)で何やらクラスのメソッド内で利用している変数を定義していますが、これは実はコンストラクタになります。CeylonのコンストラクタはJavaスタイルなコンストラクタではないためこの辺も戸惑う部分だと思います。

上記例では生成時に必ずfirstName、lastName、ageをコンストラクタの引数に指定する必要があります。また、コンストラクタの引数に指定した値を保持するフィールドはCeylonが内部で持っているため特に実装側でいちいち定義する必要はありません。

public Persion(String firstName, String lastName, int age) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}

Javaでは上記のように定義してましたがそれが不要になるということです。

上記クラスをインスタンス化して利用するにはどうすればいいかというと、これもJavaとは異なります。


Person p = Person("taro", "yamada", 23);

Ceylonにはnew演算子がないので上記のように定義します。

ここで、デフォルトコンストラクタはどうするんだとか、この時点ではフィールドの値が不定だがクラスを生成する必要があるときはどうするんだって言うと、コンストラクタのパラメータに初期値を設定するかデフォルトコンストラクタを設定するかです。

パラメータに初期値を設定した場合


class Person(String firstName="", String lastName="", Integer age=0) {
...
}

デフォルトコンストラクタ


class Person() {
...
}

Ceylonを利用してまだ日は浅いですが、デフォルトコンストラクタよりも生成引数をきちんと定義することを推奨しているように思えます。

というのもCeylonはコンストラクタの引数と同名のプロパティをおそらくコンパイル時にクラスに作成し、さらにJavaBeansの命名規則に従ったプライベートなsetter/getterメソッドを定義しているようです。

上記PersonクラスにgetFirstName()なるメソッドを定義し、firstNameプロパティの値を返すような処理を書記述した場合、IDEではPersonクラスの警告として「getLastName()は定義されています。」と出力され、Personクラスを利用する側でそのgetFirstNameを利用する定義を書くと「privateアクセスされます」と警告されます。

デフォルトコンストラクタを定義して、プロパティに自分でfirstName等必要なものを記述した場合も同様です。

最後にPersonクラスをがorg.sample.beanというパッケージにありクラス宣言にsharedアノテーションを付与した場合他のパッケージのクラスからPersonクラスを利用可能です。

では、その際のインポート宣言ですが、Ceylonでは以下のように記述します。


import org.sample.bean {Person}

void foo() {
Person p = Person(...);
...
}

{}の中にそのパッケージは以下のクラスを定義します。複数クラスを定義する場合は","で区切りで定義します。

多少慣れが必要な部分がやはりあるようです。