2024年04月27日くいなちゃん


プログラミング言語Kuin」の言語仕様7、クラスについてです。

1Kuinのクラス

Kuinでは、クラスを図1-1のように定義します。

class 名前()

    メンバ定義

end class


class 名前(継承元クラス名)

    メンバ定義

end class

図1-1: クラス定義
括弧内にクラス名を書くと、そのクラスを継承したクラスになります。 省略すると「kuin@Class」を継承します。 従って、すべてのクラスは継承元を辿ると最終的に「kuin@Class」を継承していることになります。
メンバ定義には、alias文、const文、var文、classブロック、enumブロック、funcブロックが書けます。
このうち、var文はクラスのプロパティ(メンバ変数)となり、funcブロックはメソッド(メンバ関数)となります。 それ以外はクラス内でのみ参照できる定義となります。
クラスの仕様は図1-2の通りです。
  • クラスを継承すると、継承元クラスのメンバがすべて引き継がれる。 継承元クラスが継承していた場合、継承元を辿ってメンバがすべて引き継がれる。
  • メンバのうちプロパティとメソッドに限って、定義の先頭に「+」を付けることで「パブリックメンバ」となりクラス外部に公開される。 パブリックメンバ以外はクラス外部から参照できない。
  • メソッド内では、自身のインスタンスが「me」という変数で参照できる。
図1-2: クラスの仕様

2継承

メソッドは、定義の先頭に「*」を付けることで、継承元クラスの同名メソッドを継承して上書きできます。
継承の仕様は図2-1の通りです。
  • 継承したメソッドは継承元のメソッドと、「+」の有無や、引数の型および名前や、戻り値の定義が一致していなければならない。
  • 「+」と「*」を両方指定するときは、「+*」の順序で記述する。
  • 継承元クラスに存在しないメソッドを継承しようとしたときや、継承元クラスに存在するメソッドと同名のメソッドを継承なしに定義した場合はコンパイルエラーとなる。
図2-1: 継承の仕様
継承先メソッド内から継承元メソッドを呼び出すには「super」を使います。 superは継承先メソッド内でのみ参照できる関数で、第1引数にはmeを渡して呼び出します(図2-2)。
  1. func main()
  2.   class Parent()
  3.     +func f(n: int): int
  4.       ret n * n
  5.     end func
  6.   end class
  7.  
  8.   class Child(Parent)
  9.     +*func f(n: int): int
  10.       ret super(me, n) + n
  11.     end func
  12.   end class
  13.  
  14.   var child: Child :: #Child
  15.   do cui@print(child.f(3).toStr() ~ "\n")
  16. end func
図2-2: super関数の使用例
このプログラムを実行すると、Parentクラスのfメソッドが呼び出されて「3*3=9」が計算されたのちに、Childクラスのfメソッド内で「9+3=12」が計算され、「12」が表示されます。

3ctorとcmp

kuin@Classに定義されているメソッドのうち、「ctor」「cmp」は継承可能です。

3.1ctor



ctorメソッドを継承すると、コンストラクタ(インスタンス生成時の初期化処理)が実装できます。 ctorメソッドの定義は図3-1の通りです。

func ctor()

図3-1: kuin@Class.ctorメソッドの定義
ctorメソッドを継承しない場合、何も処理をしない空のメソッドになります。 ctorメソッドの実装例は図3-2の通りです。
  1. func main()
  2.   class Test()
  3.     +var a: int
  4.     *func ctor()
  5.       do me.a :: 5
  6.     end func
  7.   end class
  8.  
  9.   var test: Test :: #Test
  10.   do cui@print(test.a.toStr() ~ "\n")
  11. end func
図3-2: ctorメソッドの実装例
このプログラムを実行すると、「#Test」の段階でTestクラスのctorメソッドが呼ばれ、最終的に画面には「5」が表示されます。

3.2cmp



cmpメソッドを継承すると、クラスの比較処理が実装できます。 cmpメソッドの定義は図3-3の通りです。

+func cmp(t: kuin@Class): int

t|比較対象の値

戻り値|比較対象の値よりも大きければ正、小さければ負、等しければ0を返す

図3-3: kuin@Class.cmpメソッドの定義
cmpメソッドを継承しない場合、比較時に例外(0xE9170004)が発生し、比較できないクラスとなります。 cmpメソッドの使用例は図3-4の通りです。
  1. func main()
  2.   class Test()
  3.     +var a: int
  4.     +var b: int
  5.     +*func cmp(t: kuin@Class): int
  6.       var t2: Test :: t $ Test
  7.       if(me.a + me.b > t2.a + t2.b)
  8.         ret 1
  9.       elif(me.a + me.b < t2.a + t2.b)
  10.         ret - 1
  11.       else
  12.         ret 0
  13.       end if
  14.     end func
  15.   end class
  16.  
  17.   var test1: Test :: #Test
  18.   do test1.a :: 2
  19.   do test1.b :: 3
  20.   var test2: Test :: #Test
  21.   do test2.a :: 4
  22.   do test2.b :: 5
  23.  
  24.   do cui@print((test1 >= test2).toStr() ~ "\n")
  25. end func
図3-4: cmpメソッドの実装例
この例ではTestクラスの比較方法を、aとbの和で定めています。

4クラスのキャスト

クラスのインスタンスは、そのクラスとその継承元クラスの型にのみ変換できます(図4-1)。
  1. func main()
  2.   class Parent()
  3.   end class
  4.  
  5.   class Child(Parent)
  6.   end class
  7.  
  8.   var parent: Parent :: #Child
  9. end func
図4-1: 継承元クラスへの代入
ただし継承先クラスに変換する場合には、「$」演算子によって明示的にキャストしないとコンパイルエラーになります。 このときインスタンスが、そのインスタンスのクラスおよび継承元クラスの型に変換される場合には変換できますが、継承先クラスに変換される場合には例外(0xE9170001)が発生します(図4-2)。
  1. func main()
  2.   class Parent()
  3.   end class
  4.  
  5.   class Child(Parent)
  6.   end class
  7.  
  8.   var parent: Parent
  9.  
  10.   do parent :: #Child
  11.   var child1: Child :: parent $ Child {キャスト成功}
  12.  
  13.   do parent :: #Parent
  14.   var child2: Child :: parent $ Child {キャスト失敗}
  15. end func
図4-2: 継承先クラスへの代入
キャストに成功するかどうかは「=$」もしくは「<>$」演算子で判断できます。
1714183954jaf