はじめに
「C#プログラマのためのF#入門」第3回目は、F#におけるオブジェクト指向の基本機能について紹介します。
.NETの基本クラスライブラリは、オブジェクト指向風に作成されているため、既存のクラス構造との互換性のためにも、オブジェクト指向のテクニックはF#において欠かせません。
F#は、C#同様に下記のようなオブジェクト指向の基本機能をサポートします。
- クラスは単一クラスのみ継承できる
- クラスは複数のインターフェイスを継承できる
- すべてのオブジェクトは共通のベースクラスとしてSystem.Object(エイリアス:obj)を持つ
加えて、F#固有の機能としては次のようなものが挙げられます。
- メソッドだけではなくプロパティやイベントの拡張が可能
- オブジェクト式(Object Expression)
- ミュータブル、イミュータブルなオブジェクト
これらのF#固有の機能を説明するにあたり、まずは、F#のオブジェクト指向基本編として、今回はオブジェクト指向の基本単位とも言える「クラス」の構造について解説したいと思います。
1. クラス
F#においても、クラスはオブジェクト指向の基本単位と言えます。クラスの定義方法、機能は大変C#のものに似ています。クラスでは、オブジェクトが使用できるフィールド、プロパティ、メソッド、イベントを宣言します。
2-1.クラスの作成
クラスを作成する際の基本構文は次のようになります。
type [アクセス修飾子] クラス名[<ジェネリックパラメータ>](コンストラクタのパラメータ(リスト)) [as セルフ識別子] = [class] [inherit ベースとなるクラスの名前(ベースコンストラクタ引数)] [letバインド] [do-バインド] メンバーリスト … [end]
(1)type キーワード
F#ではtypeキーワードを用いて、通常のクラスだけではなく抽象クラス、インターフェイス、部分的なフィールドの実装を持つクラスなど数種類のオブジェクトタイプを作成できますが、明示的にこれらを指定する必要はありません。F#のコンパイラはフィールドが宣言されている、あるいはmemberが(宣言ではなく)実装されている場合、typeキーワードで宣言しているオブジェクトをインターフェイスではなくクラスと判断します。クラス内に宣言のみされ実装されていないメンバーがある場合、クラスは抽象クラスとみなされます。
上記の構文には、角カッコを使用していろいろオプショナルなアイテムが記述されていますが、最もシンプルなクラスは次のように定義できます。
type testClass1 = class end ;;
このままでは、このクラスは何の役にも立たず、インスタンスを作成することができませんので、次にコンストラクタを追加してみましょう。
(2)プライマリコンストラクタ
他の.NET言語とは異なり、F#ではプライマリコンストラクタと呼ばれるコンストラクタが1つ存在します。プライマリコンストラクタはクラス名の後ろに丸カッコをつけ、その中にパラメータを指定することで作成できます。
では、最も単純なプライマリコンストラクタを持つクラスのインスタンスを作成してみましょう。
let インスタンス名 = new クラス名(コンストラクタ用パラメータ)
ここで言う最も単純なプライマリコンストラクタとは、引数を受け付けず、また初期化プログラムが一切存在しないコンストラクタのことです。
type testClass1() = class end;; //インスタンス作成 let testObj1 = new testClass1();;
▼
type testClass1 = class new : unit -> testClass1 //unit型のパラメータを受け取り(つまりデータを受け取らない)、testClass1型のオブジェクトを作成するコンストラクタを表す。 end val testObj1 : testClass1 //testClass1型のインスタンスtestobj1が作成された。
プライマリコンストラクタはletバインド(束縛)でフィールドを宣言し、続くdo式ですべてのインスタンスの初期化プログラム(コード)を実行します。
下記のリスト3ではプライマリコンストラクタはint型のパラメータを2つ受け取り、ミュータブル変数aとbを初期化し、それらをprintfで表示させます。
ミュータブル変数については連載の次回で詳しく解説しますが、今回は「mutableというキーワードがフィールドをミュータブル(可変)なフィールドにする」ということだけ覚えてください。
type testClass1(a1 : int, b1 : int) = //int型のパラメータを2つ受け取る。 let a = a1 + b1 //フィールドの定義(束縛) let mutable b = a1 * b1 //フィールドの定義(束縛) do printf "%d, %d" a b ;; //初期化処理
▼
type testClass1 = class new : a1:int * b1:int -> testClass1 //a1(int型)とb1(int型)のパラメータを受け取り、testClass1型のオブジェクトを作成するコンストラクタを表す。 end
インスタンスを作成してみましょう。
let testObj1 = new testClass1(10, 20) ;; //2つのint型のパラメータを渡してtestClass1型のインスタンスを作成する。
▼
30, 200 //初期化処理が行われた val testObj1 : testClass1 //インスタンスが作成された