名前空間

識別子の集合のためのコンテナ

名前空間(なまえくうかん、: namespace / name-space)は、名前の集合を分割することで衝突の可能性を低減しつつ参照を容易にする概念である。

この集合は、全事象の元の全ての組み合わせ可能なものからなる集合全体および物理的な名称を指すことが可能である。つまり英字数字記号などを組みあわせて作られる名前全てを含む集合である。名前に結び付けられる実体(変数関数)は、名前がそれぞれどの集合(空間)に属するか指定されることで一意に定まる。名前空間が異なれば同じ名前でも別の実体に対応付けられる。

身近な類似例

編集

たとえば「一郎」という人名は日本中に何人もいるため、ひとりの人間に特定することはできない。このように、同一の名前のものが複数存在し、その区別がつかない状態を名前の衝突(名前の競合)と呼ぶ。しかし、「鈴木一郎」とフルネームで呼ぶことにより他の「佐藤一郎」や「山本一郎」といった人とは区別でき、名前の衝突を避けることができる。このとき「一郎」を単純名、「鈴木一郎」を完全限定名と呼ぶ。[独自研究?]人名「一郎」は名前空間「鈴木」に属していると捉えることができる。また、同じ「鈴木」というである鈴木家の家族間では、「一郎」は暗黙的に「鈴木一郎」のことであると解釈されるため、わざわざ完全限定名の「鈴木一郎」で呼ぶ必要がない。また、予め「『一郎』とは『鈴木一郎』のことである」と宣言しておけば、その後単純名で「一郎」と呼んでも暗黙的に「鈴木一郎」だと解釈されるため、シンプルな「一郎」のみで呼ぶことができる。必要なら、「日本国東京都世田谷区○丁目○○鈴木一郎」と呼ぶことで、同姓同名の人間とも区別することができる[注釈 1]。これらの考え方は名前空間の概念に近いものである(これは分かりやすく説明するための方便であり、名前空間とは厳密にはイコールではない)。

プログラミング

編集

コンピュータプログラミングにおける名前空間は、ソースコード上で冗長な命名規則を用いなくても名前の衝突が起こらないようにし、しかもそれを容易に記述できるようにするためだけの概念であり、普通はそれ以上の意味は持っていない(上記の地名のたとえは行政上の管轄としての意味合いがあるが、プログラミング言語の名前空間には一意な名前という以上の意味合いはない)。Javaの機能、「パッケージ」では名前空間とアクセス制限、ソースファイルのディレクトリ構造の表現の機能を統合しているが、C++C#の「純粋な」名前空間はクラスやそのメンバのアクセス制限とは無関係である。Cには名前空間を複数に分割する機能が無く、名前の衝突を避けるためにはなんらかの命名規則を用いる必要がある。

通常は文脈によって定まる名前空間が暗黙に指定される。指定したい実体に対応する名前が他の名前空間にある場合は、名前空間と名前を明示的に組み合わせることで一意に特定できる。たとえば名前bazは集合Aの中ではデータ型を表し、集合Bの中では変数を表すというように指定する。[独自研究?]

同一のスコープに、異なる種別の構文要素で同じ名前(識別子)が出現する場合、文脈で区別する言語もある。

以下はJavaの例である。

public class SomeClass {
    void baz() { System.out.println("baz() is called."); }
    int baz;

    public void doSomething1() {
        baz();
    }

    public void doSomething2() {
        baz = 100;
    }
}

この例では同じbazという名前を持つメソッド(関数)とフィールド(変数)が宣言されているが、doSomething1()メソッド内に記述された名前bazはメソッド呼び出し式のセパレータ()を伴っており、またdoSomething2()メソッド内に記述された名前bazには=演算子によって数値の代入がされており、Java処理系(コンパイラ)はそれぞれがメソッド名および変数名であると文脈(context)から推定できる[1]。ただし、このような曖昧な命名は、Java言語仕様上は合法ではあるものの、コードの可読性やメンテナンス性を損なうため、一般的には避けるべきである。違反コードには警告を出す静的コード解析ツールもある[2]。なお、C/C++やC#、Schemeなどでは、上記のように同じ名前の関数(メソッド)と変数(フィールド)が同じスコープで再定義された時点でエラーになるか、値が束縛された名前で関数を呼び出そうとするとエラーになる。他の例としては、関数(メソッド)内で、その関数(メソッド)と同じ名前のローカル変数を定義した場合、文脈によって暗黙的に区別できるならば(それが望ましいかどうかは別として)同じ名前を使用することができる。

しかし、ひとつの名前空間の中で同名の型/同名の変数/同名の関数を複数定義すると問題が発生する。次の疑似コードのように、同じ名前の関数、Hogeを2つ定義したとする。

void Hoge() {}
void Hoge() {}
void Main() {
    Hoge();
}

この場合、2つ目のHoge関数が定義された時点で、処理系は多重定義としてエラーを出す。特に複数のチームで開発を行っていると名前の衝突が起きやすい。次の例は、別々のチームで書かれた2つの関数Hoge()の名前の衝突を避けるため、関数名の先頭に「チーム名_」とつけるように取り決めた場合のものである。

void TeamA_Hoge() {}
void TeamB_Hoge() {}
void Main() {
   TeamA_Hoge();
   TeamB_Hoge();
}

これでも名前の衝突は避けられるが、呼び出しでは常にチーム名まで含めた名称で記述しなければならないため、記述も面倒でソースコードは読みにくくなってしまう。また、両方の関数をincludeしないような時には名前の衝突は起こらず、せっかくの命名規則も取り越し苦労に終わってしまう。このような場合は、2つのクラスHogeを別々の名前空間に入れる。

namespace TeamA {
    void Hoge() {}
}
namespace TeamB {
    void Hoge() {}
}
void Main() {
    TeamA.Hoge();
    TeamB.Hoge();
}

こうすると、上の関数Hogeの名前はTeamA.Hoge、下の関数Hogeの名前はTeamB.Hogeとなり、両者を区別できるようになる。複数のチームで開発を行う場合は、チームごとに使用する名前空間を決めておけば名前の衝突は起こらないので、それぞれのチームが自由に名前を付けることができる。しかし、ひとつのソースコード中でTeamA.HogeとTeamB.Hogeの片方だけを使う場合は、完全限定名での記述は冗長な表現となってしまう。以下の疑似コードでは、using namespace指令を使って、名前空間内の識別子をインポートし、修飾されていない単純名を使用できるようにしている[注釈 2]

using namespace TeamA;
void Main() {
    Hoge();
}

処理系はusing namespace指令から単純名Hogeの完全限定名がTeamA.Hogeであることを推定し、単純名Hogeは完全限定名TeamA.Hogeに補完されてコンパイルが成功する。また、同一の名前空間内からの呼び出しでは単純名は自動的に補完されるため、常に単純名で記述することができる。下の例では、関数Hogeと関数Mainは同一の名前空間TeamAに属しているため、関数Mainからは単純名での記述で関数Hogeを呼び出すことができる。

namespace TeamA {
    void Hoge() {}
    void Main() {
        Hoge(); // TeamA.Hogeの呼び出しだとみなされる
    }
}

オーバーロードをサポートする言語であれば、引数などのインターフェイスが異なる場合に限り、異なる関数や異なるメソッドに同じ名前を使用することができる。どのオーバーロードが使用されるかは、呼び出し時に渡す実引数の型などから文脈によって判断される。

プログラミング以外の名前空間

編集

これら名前空間およびそれに付随する概念は、プログラミング言語に限らず活用されている。例えば、EメールアドレスURIも名前空間と同等の論理で組み立てられていることはよく知られており、このような命名の仕組みによって名前を区別、分類するようなものを、広く名前空間と呼ぶこともある。

  1. URIのスキームの名前空間はIANAが管理している。
  2. XMLの名前空間は、要素名などが重複することのないように整理された空間。xmlns属性などで名前空間を宣言する。例えば、「<html:p>」と記述されたタグでは、「html」が名前空間接頭辞で「p」が要素名を表す。
  3. ウィキペディアを含むMediaWikiの使用サイトにおいては、Help:名前空間のようなページの種類を区分する名前空間の一覧がある。例えば「Help:名前空間」という項目名においては、「Help」が名前空間、「名前空間」が名前である。この記事自体が、同名の項目でも、名前空間により書かれている内容が異なるものの一例として挙げることができる。

脚注

編集

注釈

編集
  1. ^ これは住所が同一の同姓同名人物はいないという前提に基づいている。例えば欧米では親子で同姓同名をつけることもあり、そのような親子が同居しているケースでは、住所による区別だけでは個人が一意に定まらず破綻する。同姓同名人物がルームシェアをしている場合も同様に破綻する。
  2. ^ C++の場合はusing宣言で名前空間内の特定の識別子を、using namespace指令で名前空間全体をインポートする[3]。Javaの場合はimport文を使ってパッケージ内の特定の型またはすべての型をインポートする[4]。C#ではusing指令を使うことで、その名前空間内で定義されたすべての型をインポートする[5]。なお、C++では名前空間スコープに直接変数や関数を定義することができ、またそのような関数は自由関数(free function)とも呼ばれる[6]。一方、JavaやC#では名前空間スコープに直接フィールドやメソッドを定義することはできず、必ず何らかの型に所属させる必要がある。

出典

編集

関連項目

編集