Adaの日本語情報を増やす(5)
今回はフェイントでOOPについてまとめる、その1。ランデブーとか後回し。
AdaにおけるOOPは一応のところ「Ada95から導入された」ということになっている。とはいえ、Ada83の段階でもすでにOOPの素養――具体的には「派生型」を持っており――Ada95におけるOOP対応もこの派生型を拡張して行われた。
派生型が持つ強力な性質として、「派生元の型」に対して定義されている副プログラム(引数にその型を持ったprocedure
や、返値にその型を持つfunction
など)を自動的に継承する点が挙げられる。ただし、継承されるのはその「派生元の型」が定義されているパッケージ内で定義された副プログラムに限る。
口で説明するより次のサンプルコードを読んだ方が速いだろう。
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure OOP1 is
package Drinks is
type Drink is private; -- カプセル化のため内部構造は開かさない
-- 同一パッケージに存在する以下の3つの副プログラムが派生先に継承される
procedure Add(O : in out Drink; V : in Positive);
procedure Spilt(O : in out Drink; V : in Positive);
function Get_Volume(O : in Drink) return Natural;
-- せっかくprivateなのに何でスペックに実定義を書くのか?
-- コンパイラの都合上らしいですよ
private
type Drink is record
Volume : Natural := 0;
end record;
end Drinks;
-- スペック通りに実装します。
package body Drinks is
procedure Add(O : in out Drink; V : in Positive) is
begin
O.Volume := O.Volume + V;
end Add;
procedure Spilt(O : in out Drink; V : in Positive) is
begin
O.Volume := O.Volume - V;
end Spilt;
function Get_Volume(O : in Drink) return Natural is
begin
return O.Volume;
end Get_Volume;
end Drinks;
-- 別パッケージで派生した場合
package Coffees is
-- 派生型の宣言だけする
type Coffee is new Drinks.Drink;
end Coffees;
-- 主プログラム内で派生した場合
type Beer is new Drinks.Drink;
-- 実験用に変数を定義
Cup_C : Coffees.Coffee; -- 当然パッケージ名から
Cup_B : Beer ;
begin
-- 自分で実装していないのに、Drinks内の副プログラムがCoffeesに継承されている!
Coffees.Add(Cup_C, 200);
Coffees.Spilt(Cup_C, 100);
Put("Cup_C: "); Put(Coffees.Get_Volume(Cup_C)); New_Line;
-- こちらもやはり継承されているのが分かる
Add(Cup_B, 100);
Spilt(Cup_B, 50);
Put("Cup_B: "); Put(Get_Volume(Cup_B)); New_Line;
-- Cup_C: 100
-- Cup_B: 50
end OOP1;
Coffees.Add
やSpilt
は定義されていないにもかかわらず、派生型のおかげで問題なく使用できるのが分かると思う。パッケージCoffees
には次のような副プログラムが自動的に定義されたと思えば良い。
procedure Add(O : in out Coffee; V : in Positive);
procedure Spilt(O : in out Coffee; V : in Positive);
function Get_Volume(O : in Coffee) return Natural;
なお、あくまでもCoffees.Cofee
用の副プログラムがCoffees
内に自動的で定義されるだけであり、派生元の副プログラムに派生先の型を許容する機能があるわけではない。つまり、次のコードはコンパイルエラーになる。
Drinks.Add(Cup_B, 100);
Drinks.Add
が引数に取るのはあくまでもDrinks.Drink
であるので、Coffees.Coffee
型であるCup_Bを操作したいのならば、Coffees.Add
を呼び出す必要がある。この(Javaプログラマーが首をかしげそうな)点は、後述のClass Wide型と関連する重要な性質なので覚えておいて欲しい。
さて、本来派生型は型の安全性を高めるために存在するようだが(たとえば、間違ってBeerとVineを混ぜたりしないように)、今見ればOOPにおける継承に近い性質を持っているのが分かると思う。この点を踏まえつつ、次はAda95のtagged recordに続きます。