proglog

主にプログラミングに関する断片的メモ

[devel][rakcet] Racketのクラスの基礎っぽいことのメモ(1)

「あ…ありのまま今起こった事を話すぜ!
『俺はGaucheの最新版をcygwinにインストールする作業をしていたと思ったら、いつの間にかRacketのクラスを調べていた』。
な…何を言っているのかわからねーと思うが、
俺も」
という訳で、Racket(旧名PLT Scheme)のオブジェクト指向部分、クラスの作り方をちょっと調べたのでメモ。


参考にしたのは、Racketから、


今のところ分かった特徴としては、

  • 最上位クラスはobject%
  • 多分単一継承
  • javaのようにインターフェイスがある
  • mixinの構文があるみたい
  • アクセスコントロール的なものがなんかちょっと多い
  • でもprotectedやパッケージスコープみたいなものはないような?

などなど。

まずは基本型

(define exclass0%
  (class* object% ()
    ;スーパークラスの初期化
    (super-new)
    (init [arg1 1] [arg2 2])
    (field [pblcfield1 arg1] [pblcfield2 arg2])))

クラスを定義して、シンボルにバインドしている。
クラス名は規約により"%"で終わる。

(class* スーパークラス名 (インターフェイス名)
  (super-new スーパークラス生成)
  (init 初期化変数:オブジェクト生成時に渡す、初期化のための引数)
  (field パブリックなフィールド、つまりインスタンス変数の宣言)
  )

class*じゃなくて、classというのもある。
この場合は、実装するインターフェイス宣言部分が無い。
でも、いくつかのソースではインターフェイスを使ってなくてもあえてclass*を使ってあった。
だから、こっちで。


スーパークラスの生成は必ず行う。
処理は上から、書いた順に行われる。


パブリックなフィールドというのは、パブリックにアクセスが出来るデフォルトのアクセサが使えるフィールド、ということ。

オブジェクト生成

以下、実行結果は;;;>で示す

(define exobj0 (new exclass0%))
;フィールド値取得
(get-field pblcfield1 exobj0)
;;;>1
;フィールド値変更
(set-field! pblcfield1 exobj0 2)
(get-field pblcfield1 exobj0)
;;;>2

デフォルトのゲッターとセッター。

オブジェクトの生成と初期化

名前付き引数
(define exobjn (new exclass0% [arg2 20] [arg1 10]))
(get-field pblcfield1 exobjn)
;;;>10
(get-field pblcfield2 exobjn)
;;;>20

newを使ってオブジェクトを生成する。

位置による引数
(define exobjm (make-object exclass0% 30 40))
(get-field pblcfield1 exobjm)
;;;>30
(get-field pblcfield2 exobjm)
;;;>40

make-objectを使う。

位置と名前
(define exobji (instantiate exclass0% [50] [arg2 60]))
(get-field pblcfield1 exobji)
;;;>50
(get-field pblcfield2 exobji)
;;;>60

instantiateを使う。

ちなみにこれらの頭にsuper-を付けたものをスーパークラスの生成に使う。

フィールドと初期値

初期化変数を介さずにフィールドを宣言、初期化する。

(define exclass01%
  (class* object% ()
    ;スーパークラスの初期化
    (super-new)
    ;フィールドの宣言
    (init-field  pblcfield1 pblcfield2)))


(define exobj01-1 (make-object exclass01% 10 20))
(get-field pblcfield1 exobj01-2)
;;;>10
(get-field pblcfield2 exobj01-2)
;;;>20

init-filedを使うと、フィールドと、それがオブジェクト生成時に直接引数を受け取ることを同時に宣言できる。


デフォルト値を付けて宣言することもできる。

(define exclass02%
  (class* object% ()
    (super-new)
    (init-field  [pblcfield1 30] [pblcfield2 40])))


(define exobj02 (new exclass02%))
(get-field pblcfield1 exobj02)
;;;>30
(get-field pblcfield2 exobj02)
;;;>40

プライベートなフィールドとメソッド

(define exclass1%
  (class* object% ()
    (super-new)
    (define prvtfield1 1)
    (define/public (getprvt1)
      prvtfield1)))

(define ex1obj (new exclass1%))
(send ex1obj getprvt1)
;;;>1

defineとかdefine-valueで宣言したフィールドはプライベートになる。
これは、デフォルトのアクセサは使えず、javaと同じように、直接アクセスできるのは、そのクラスからのみとなる。


しかし、自前でアクセサを用意すれば、当然それを使ってアクセスは出来る。


define/publicがメソッドの宣言。
どこからでも使えるパブリックなアクセスが可能。


メソッドを呼び出すのは、(send 対象オブジェクト メソッド名)。

プライベートフィールドの初期化

defineに直値を与える他に、初期化変数を使って、オブジェクト生成時に値を受け取ることもできる。

(define exclass1-2%
  (class* object% ()
    (super-new)
    (init [arg1 10])
    (define prvtfield1 arg1)
    (define/public (getprvt1)
      prvtfield1)))

(send (new exclass1-2%) getprvt1)
;;;>10
(send (make-object exclass1-2% 20) getprvt1)
;;;>20

プライベートメソッド

define/privateを使って宣言する。
java同様、そのクラスの中からしか使えない。

(define exclass1-3%
  (class* object% ()
    (super-new)
    (init [arg1 10])
    (define prvtfield1 arg1)
    (define/public (getprvt1)
      (privmethod1)
      prvtfield1)
    (define/private (privmethod1)
      (set! prvtfield1 (+ prvtfield1 10)))))

(send (new exclass1-3%) getprvt1)
;;;>20
(send (make-object exclass1-3% 20) getprvt1)
;;;>30

プライベートなメソッドを、パブリックなメソッドから呼び出している。


次回は継承