D 言語では、何らかの宣言を修飾する一連のブロックを属性と呼んでいます。 ここで言う属性は、一般オブジェクト指向(設計論)で使われる用語の属性や XML で使われる用語の属性、Microsoft が .NET で使っている用語の属性などとは異なります。
属性は宣言の手前に記述し、その後の宣言に影響を及ぼします。 一般的な属性は次のように記述することになるでしょう。
属性 宣言
この場合、直後に指定した宣言に属性がかかります。 単一の宣言にのみ属性を指定したい場合はこのように記述することになるでしょう。 複数の宣言に属性を指定する場合は次のように指定します。
属性: 宣言1 宣言2 ...
この記述方法では、次の } までのすべての宣言に属性が指定されると解釈されます。 属性の影響範囲をより明確にするために、ブロックで指定することもできます。
属性 {
宣言1
宣言2
...
}
この場合、ブロックの中のすべての宣言に属性がかかります。
アクセス保護属性は、宣言した識別子にアクセスできるスコープの範囲を指定するものです。 通常はクラスのメンバに対して指定する属性ですが、モジュールスコープに指定することも可能です。
private 属性は、クラスのメンバに指定した場合、そのクラス内からのみアクセスできるメンバであることを表します。 private 属性を持つフィールドやメソッドは、サブクラスも含めてクラスの外部からアクセスすることはできません。 モジュールスコープのクラスや関数に指定した場合、そのモジュール(ファイル)からのみアクセスできることを表しています。 モジュールメンバに private を指定するのは C 言語で static 修飾子を指定する宣言と同一です。
protected 属性は、同じクラス内かそのサブクラスからのみアクセスできるメンバであることを表しています。 private メンバと異なるのはサブクラスからもアクセスできるようになることです。 クラスの外部からアクセスすることはできません。
public 属性は、その識別子がすべての場所からアクセスすることができることを表します。 通常、メソッドやプロパティの多くは public となるでしょう。
module Geom;
class Point {
private int x , y;
public this(int x , int y) {
this.x = x;
this.y = y;
}
public void X(int x) { this.x = x; }
public int X() { return x; }
public void Y(int y) { this.y = y; }
public int Y() { return y; }
}
import std.stream;
import Geom;
int main() {
Point pt = new Point(64 , 128);
//stdout.printf("X=%d,Y=%d\n" , pt.x , pt.y); //エラー
stdout.printf("X=%d,Y=%d\n" , pt.X , pt.Y); //OK
return 0;
}
このプログラムの Geom モジュールの Point クラスでは、int 型の x と y フィールドに対して private 属性が指定されています。 そのため、他のモジュールから Point クラスの x や y フィールドにアクセスすると、コンパイルエラーとなってします。 Geom モジュールを利用している main() 関数から x や y フィールドにアクセスすると確認できるでしょう。
また、Point クラスのコンストラクタや X 及び Y プロパティには public 属性が指定されています。 そのため、これらのメンバには他のモジュールからでも自由にアクセスすることができます。 Point クラスの public 属性の指定は多くの宣言に利用されているため、次のようにブロック単位で記述することでまとめることも可能です。
module Geom;
class Point {
private int x , y;
public {
this(int x , int y) {
this.x = x;
this.y = y;
}
void X(int x) { this.x = x; }
int X() { return x; }
void Y(int y) { this.y = y; }
int Y() { return y; }
}
}
どちらを用いるかは開発者の好みでしょう。
クラスのインスタンスではなく、クラスのスコープに直結している変数やメソッドは static 属性を用いて宣言することができます。 static 属性による修飾で宣言されたメンバは、インスタンスごとではなく常にクラスに対して 1 つの実体だけが存在します。 クラスのスコープ内にあることを除いて、メンバはグローバルな存在となります。
class A {
public static int a;
}
static 属性ではないメンバ変数はインスタンスごとに領域が確保されますが、static 属性で修飾されているメンバ変数 a はクラス A に結びつきます。 このような static 属性が指定されているメンバのことを、一般的にクラスメンバと呼びます。 static メンバ変数はクラス変数、static メソッドはクラスメソッドと呼ぶことができるでしょう。 これに対して通常のメンバはインスタンスメンバと呼びます。 メンバ変数はインスタンス変数、メソッドはインスタンスメソッドと呼ぶことができます。
static 属性は public などのアクセス保護属性と合わせて指定することができます。 static なメンバは this からアクセスすることができず、型名から直接アクセスしなければなりません。
A.a = 10;
stdout.printf("A = %d" , A.a);
このように、型名 A から static メンバにアクセスしなければなりません。 static メンバは this を持たないためクラスの内部で this からアクセスすることはできません。 しかし、外部のコードがオブジェクトから static メンバにアクセスすることは可能です。
A obj = new A();
obj.a = 10;
stdout.printf("A = %d" , A.a);
上記のコードでは obj.a という形で static メンバ a にアクセスしています。 しかし、これは A.a という表記と意味は同じです。 可読性を考慮した場合、通常のインスタンスメンバと区別が難しくなるので、型名からアクセスする記法が適切だと考えられます。
import std.stream;
class Point {
private static int x , y;
public static void X(int x) { Point.x = x; }
public static int X() { return x; }
public static void Y(int y) { Point.y = y; }
public static int Y() { return y; }
public static void Print() {
stdout.printf("Point X=%d,Y=%d\n" , X , Y);
}
}
int main() {
Point.X = 10;
Point.Y = 100;
Point.Print();
Point pt = new Point();
pt.Print();
return 0;
}
Point X=10,Y=100 Point X=10,Y=100
このプログラムでは、Point クラスのメンバがすべて static 属性が指定されているクラスメンバとなっています。 そのため x、y メンバ変数や、X、Y プロパティ、Print() メソッドは常にクラスに対して 1 つの実体しか存在しません。 インスタンスをいくつ生成しても、すべてのインスタンスはこれらのクラスメンバを共有してしまいます。
実行結果を見れば、Point クラス型の名前からメンバにアクセスした場合と、main() 関数内で生成したインスタンスからアクセスした場合の双方が同じ結果となっていることがわかります。
コンパイル時に完全に固定することができる定数に対しては const 属性を指定することができます。
const int a = 100;
const 属性はグローバル変数、ローカル変数、メンバ変数などの変数宣言に指定することができます。 通常この変数には変数初期子 = で目的の値で初期化しなければなりません。 初期化を省略すれば D 言語が定める初期値となりますが、この変数は初期化以降変更することができなくなります。 const 変数に代入を試みた場合、コンパイルエラーとなります。
import std.stream;
const int a = 10;
class A {
public static const int a = 100;
}
int main() {
//a = 1000; //エラー
//A.a = 50; //エラー
stdout.printf("a = %d\n" , a);
stdout.printf("A.a = %d\n" , A.a);
return 0;
}
a = 10 A.a = 100
このプログラムのグローバル変数 a 及び A クラスのクラス変数 a は双方ともに const 属性が指定されています。 const 属性の変数は定数として扱われるため、参照は可能ですが値を変更することはできません。