クラス
LuneScript はクラスベースのオブジェクト指向プログラミングをサポートしています。
定義とインスタンス化
class キーワードで定義し、new でインスタンス化します。
class MyClass {
pri let val: int;
// コンストラクタ
pub fn __init( val: int ) {
self.val = val;
}
// メソッド
pub fn func(): int {
return self.val;
}
}
let obj = new MyClass( 10 );
print( obj.func() ); // 10
- コンストラクタ:
__initという名前で定義します。すべてのメンバ変数はここで初期化される必要があります。 - self: 自分自身(インスタンス)への参照は
selfを使います。 - アクセス制御:
pub(公開),pro(保護 - サブクラスまで),pri(非公開),local(モジュール内) があります。デフォルトはpriです。
アクセサ
メンバ定義時に {getter, setter} の形式でアクセサを自動生成できます。
class Data {
pri let mut val: int {pub, pub}; // getter=pub, setter=pub
pub fn __init( val: int ) { self.val = val; }
}
let mut d = new Data( 1 );
$ 演算子
自動生成された getter にアクセスするには、メンバ名の前に $ を付けます。これは内部的には get_メンバ名()
メソッドの呼び出しに変換されるシンタックスシュガーです。
setter は set_メンバ名 というメソッドが生成されます。
なぜ「プロパティ」ではないのか?
一部の言語にあるような「プロパティ(直接アクセスしているように見えて内部でメソッドが呼ばれる形式)」をあえて採用していません。これにより、単なるデータの代入・参照なのか、副作用のあるメソッド呼び出しなのかがソースコード上で明確に判別できるようになっています。
class Data {
pri let mut val: int {pub, pub};
pub fn __init( val: int ) { self.val = val; }
}
let mut d = new Data( 1 );
print( d.$val ); // getter 呼び出し ($記号)
d.set_val( 20 ); // setter 呼び出し (set_メンバ名)
print( d.$val );
アクセス制御の省略
setter が不要な場合は、2つ目の指定を省略または non を指定します。
class AccessorTest {
pri let val1: int {pub}; // getterのみ (setterなし)
pri let val2: int {pub, non}; // 同上
}
Immutable な戻り値
getter の戻り値を Immutable にしたい場合は、& を付けます。
class ListWrapper {
pri let mut list: List<int> {pub&, pub};
}
// $list は &List<int> (read-only) を返すので、変更できない
継承とオーバーライド
extend で単一継承が可能です。メソッドのオーバーライドには override キーワードが必須です。
class Base {
pub fn func() { print( "Base" ); }
}
class Sub extend Base {
pub override fn func() {
super(); // 親クラス呼び出し
print( "Sub" );
}
}
サブクラスのコンストラクタでは、先頭で super() を呼び出す必要があります。
抽象クラス
実装を持たないメソッド (abstract fn) を持つクラスは、abstract class として定義する必要があります。
abstract class Animal {
pub abstract fn cry();
}
インタフェース
多重継承の代わりにインタフェースを使用できます。インタフェースは実装を持ちません。
interface Runner {
pub fn run();
}
class Dog extend (Runner) {
pub fn run() { print( "Run!" ); }
}
複数のインタフェースを実装する場合は extend (IF1, IF2) のように記述します。
Advertise (委譲)
コンポジション(包含)したオブジェクトのメソッドを、あたかも自分のメソッドのように公開(委譲)する機能です。
class Inner {
pub fn foo() { print("Inner.foo"); }
}
class Wrapper {
pri let inner: Inner;
advertise inner; // inner の public メソッドを Wrapper のメソッドとして公開
}
Mapping
extend (Mapping) を宣言することで、Map (JSON的なオブジェクト) と相互変換が可能になります。
class User extend (Mapping) {
let name: str {pub};
let age: int {pub};
}
// JSON (Map) から生成
let user = unwrap User._fromMap( { "name": "John", "age": 20 } );
// Map へ変換
let map = user._toMap();
プロトタイプ宣言
メソッドの実装を後で記述(または別ファイルに記述)する場合に使用します。相互参照が必要な場合に便利です。
class Test {
pub fn func(); // 宣言のみ
}
// 実装
pub fn Test.func() {
print( "impl" );
}