公称的型付けと構造的型付け
プログラミング言語では、型の区別と互換性の判定に際して、主に二つのアプローチがあります。
- 公称的型付け(Nominal Type System)
- 構造的型付け(Structural Type System)
みなさんは、自分が使用しているプログラミング言語はどちらのアプローチを採用しており、また、それぞれのアプローチがどういうものなのか理解しているでしょうか?
かくいう私もそんなに意識せずに使用していました。
改めて「型の互換性の判別」「型の階層関係の判別」という二つの観点から両者を比較しつつ、理解を深めてみようと思います!
公称的型付け
公称的型付けでは、その名の通り「型の名前」を見て型の区別と互換性を判定します。
このアプローチを採用している代表的な言語は、java、PHPなどです。
型の互換性
型の互換性は、名前を見て決める
class Cat {}
class Dog {}
class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = cat; // エラーが発生する
}
}
型の階層関係
型の階層関係の判定は、extends
キーワードで宣言する(java
の場合)
class Animal {}
class Dog extends Animal{}
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
Animal animal = dog; // OK
}
}
extends
で宣言されていない場合、両者の親子関係は判別されない。
class Animal {}
class Dog {}
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
Animal animal = dog; // エラーが発生する
}
}
このように公称的片付けでは、extends
を使用したクラスの継承によって、階層関係を表現します。
これを公称的部分型と呼びます。
構造的型付け
構造的型付けでは、名前ではなく、「型の構造」を見て型の区別と互換性を判定します。
「構造を見る」とは、型が持つプロパティやメソッドの構造によって型同士の区別、互換性の判定を行うということです。
このアプローチを採用している代表的な言語は、TypeScript, Goなどです。
型の互換性
型が同一かどうかは、型の構造を見て判定する
class Cat {
name: string
walk() {}
}
class Dog {
name: string
walk() {}
}
const cat = new Cat();
const dog: Dog = cat; // エラーにはならない
公称的型付けの時はクラス名が違えば区別されてエラーになりましたが、構造的型付けでは、クラス名が違っても構造が同じであれば互換性が認められます。
型の階層関係
型の階層関係の判定は、型の構造を見て判定する
class Animal {
walk() {}
}
class Dog {
name: string
constructor(name: string) {
this.name = name;
}
walk() {}
}
const animal: Animal = new Dog("pochi");
公称的型付けの場合は、extends
キーワードがない場合は構造が同じでも継承関係が認められませんでした。
構造的型付けでは「型の構造」に着目し、Dog
クラスがAnimal
クラスを部分的に実装しているため、だAnimal
型の変数にDog
型のインスタンスを代入できます。
これを、構造的部分型といいます。
まとめ
公称的型付け | 構造的型付け | |
---|---|---|
型の互換性 | 名前を見て判定 | 構造を見て判定 |
型の階層関係 |
extends で宣言 |
構造を見て判定 |
ありがとうございました!