この項目では、型システムにおける抽象型について説明しています。実装と独立したデータ型については「抽象データ型 」をご覧ください。
抽象型 (ちゅうしょうがた、英 : abstract type )とは、コンピュータプログラミング の型システム のうち、名前的型システム (nominal / nominative type system ) [ 注釈 1] [ 1] における型の一種であり、直接インスタンス 化することができないという特徴を持つ。対義語は具象型 (英 : concrete type )であり、具象型はインスタンス化することができる。抽象型の変数に格納された実際の値は、何らかの存在型 (英語版 ) (existential type) を持つ[ 2] 。
抽象型は実装を提供しないか、あるいは不完全な実装を提供する。具体的な形態や仕様はプログラミング言語ごとに異なるが、いくつかの言語において、実装を持たない抽象型はインタフェース (interface) やプロトコル (英語版 ) (protocol) などと呼ばれている。クラスベースのオブジェクト指向プログラミング 言語では、抽象型は抽象クラス (abstract class) [ 注釈 2] として実装され、具象型は具象クラス (concrete class) として実装される。
例えばJava での抽象型は、抽象クラス とインタフェース である。ただし抽象クラスはabstract
という宣言が付与されたクラスのことであり、抽象メソッドを含むこともあれば、含まないこともある[ 3] 。また、メソッドのシグネチャを一切含まない空のインタフェースを定義することもできる。
抽象型の表記方法
抽象クラスは、以下のようないくつかの方法で生成され、示され、シミュレートされる。
Java やC# では、クラス定義の際に明示的にキーワード abstract
を付与することで抽象クラスであることを示す。
クラス定義の中で、クラスがそのプロトコルの一部として受容することを宣言されているが、実装は提供されないメソッド (抽象メソッド、C++ では純粋仮想関数 と呼ぶ)を含む場合、そのクラスは抽象クラスである。
抽象型を継承 するが、実装が必要な機能(抽象メソッド)をすべてオーバーライド しないクラスもまた抽象クラスである。
Smalltalk などの動的型付き言語では、自分自身 (this) へ送信するメソッドが実装されていない場合、そのクラスは抽象クラスと見なせる。(ただし、このような実装は単なるバグ の可能性もあり、実行してみるまでエラーであることが検出できない。)
抽象型の性質
抽象型は、静的型付け のオブジェクト指向言語において重要な機能である。動的型付け 言語の場合は、ダック・タイピング があるので抽象型は不要であり、等価な機能は存在しないものが多かったが、名前的型付け (nominal typing) の安全性や利便性を享受する観点から抽象型を導入した事例もある[ 4] 。
派生型のないクラスを「リーフクラス」とし、それ以外を抽象クラスに分類している書籍もある[ 5] 。
抽象型は、派生型が実装すべきメソッド群をプロトコルとして規定することができる。抽象型のインスタンス は直接生成できず、派生型は全ての必要な機能を実装しなければならない。このことはプログラムの正当性 を保証する役割を担っている。
抽象型の種類
この節には独自研究 が含まれているおそれがあります。 問題箇所を検証 し出典を追加 して、記事の改善にご協力ください。議論はノート を参照してください。(2021年11月 )
抽象クラス (実装継承と型継承の混合)
オブジェクト指向言語 における抽象クラス とは、abstract
を明示的に宣言されたクラス か、抽象メソッド(メソッドシグネチャの宣言のみで、実際のコードが実装されていないメソッド)を含むクラスである。通常のクラスと異なり、インスタンス化はできない。
実装の多重継承 には技術的問題があるため、多くのオブジェクト指向言語では、直接には単一の基底クラスしか継承できない。複数の基底型から成る派生型 の定義 (subtyping) をサポートするため、多重継承を「実装継承」ではなく「型継承」に限定する目的で、抽象クラスとは別の純粋な抽象型(インタフェース)を定義する機構を備えた言語もある[ 6] 。
インタフェース (純粋な型継承)
Java などにはインタフェース がある。Javaのインタフェースはメソッドのシグネチャや定数を持つことができるが、メソッドの実装や変数(フィールド )を持つことはできない。Javaのクラスは複数のインタフェースを実装 (implement) できる。インタフェースは抽象メンバーのみを含む抽象クラスに似ているが、多重継承できる点が異なる。
Java 8やC# 8のように、言語仕様の改訂によりインタフェースに静的メソッドを定義したり、デフォルト実装を提供したりすることもできるようになったケースもある[ 7] [ 8] 。
Javaにおける抽象クラスは、インタフェースを実装し、いくつかのメソッドのシグネチャを定義していることもあるが、一方でキーワード abstract
により抽象化されたままのメソッドを持っている。クラスが実装すると宣言したインタフェースのすべてのメソッドの実装を提供していない場合、そのクラスはabstract
として宣言されなければならない。
C#では、クラスが実装すると宣言したインタフェースのすべてのメソッドの実装を提供する必要がある。
プロトコル (純粋な型継承)
Objective-C とSwift にはプロトコルがある[ 9] [ 10] 。プロトコルはメソッドやプロパティの宣言を持つことができるが、それらの実装や変数(状態)を持つことはできない。Objective-Cのクラスや、Swiftのクラス・構造体・列挙型は複数のプロトコルを採用 (adopt) できる。
Objective-CとSwiftのプロトコルでは、インスタンスメンバーだけでなく、クラスメンバー(静的メンバー)の宣言を持つこともできる。プロトコルを採用する型は、それらの実装を提供しなければならない。プロトコルのメンバーにはrequiredとoptionalの種別があり、requiredの実装は必須だが、optionalの実装は任意である。
Swift 2.0では、プロトコル拡張 (protocol extension) により、既存のプロトコルにメソッドの実装やcomputedプロパティの実装を追加することができるようになった[ 11] [ 12] 。プロトコル拡張により、メソッドやプロパティのデフォルト実装を提供することもできる。Swiftの「拡張」はObjective-Cの「カテゴリ」に似た言語機能であり、既存の型に対して変更を加えることなく機能を追加することができる[ 13] 。ただし、プロトコル自身は依然として実装や状態を持つことはできない。
トレイト (Mix-in での機能)
トレイト が抽象型に当たるのかは諸説分かれる。トレイトは構造的型付け (英語版 ) に準拠しており、その本質は、クラスに機能注入するためのメソッドの集合体だからである。これをRuby はモジュールと呼び、Raku はロールと呼んでいる。トレイトはSmalltalk 処理系由来であり、Mix-inの作法もヒントになっていた。[要出典 ]
多重継承前提のトレイトは、主に関数型 を取り入れたオブジェクト指向言語 で様々に解釈されるようになり、その中にはインタフェースとの類似形もあったので、これが抽象型と見なされる一因になっている。 [要出典 ]
型クラス (アドホック多相由来のジェネリック ・サブタイピング )
元々の型クラスは純粋関数型言語で、関数のパラメータ型に制約(constraint)を付加して関数オーバーロード を可能にするための手段であり、そのオーバーロードをジェネリック 化していた。制約の付加はアドホック多相と解釈された。その制約を、サブタイピング されるジェネリック 抽象型にしてデータクラスやレコード型 や構造体 に継承できるようにしたものが、オブジェクト指向言語で言われる型クラスになっている。 [要出典 ]
脚注
注釈
^ 英 : nominal は公称的、名目的または記名的とも訳される。
^ 抽象基底クラス (abstract base class) とも。
出典
参考文献
Types and Programming Languages by Benjamin Pierce (MIT Press 2002) [1]
More Effective C++: 35 New Ways to Improve Your Programs and Designs by Scott Meyers (1995) ISBN 0-201-63371-X
外部リンク
関連項目