オブジェクト指向: プロパティ・メソッド
目次
クラス変数、インスタンス変数
# クラス変数
class_var = “Parent class_var”
def __init__(self):
# インスタンス変数
self.instance_var = “Parent instance_var”
class Child(Parent):
# クラス変数
class_var = “Child class_var”
def __init__(self):
# インスタンス変数
self.instance_var = “Parent instance_var”
print(Parent.class_var)
→Parent class_var
print(Parent.instance_var)
→エラー
parent = Parent()
print(parent.class_var)
→Parent class_var
print(parent.instance_var)
→Parent instance_var
print(Child.class_var)
→Child class_var
print(Child.instance_var)
→エラー
child = Child()
print(child.class_var)
→Child class_var
print(child.instance_var)
→Parent instance_var
補足
pythonではインスタンス変数を明示的に定義する事はできない
コンストラクタでself.〜で定義する
注意
pythonではクラス内部からクラス変数にアクセスする場合でもself.〜と書く
この為、クラス変数と同名のインスタンス変数をコンストラクタ内で定義すると、インスタンス、クラス両方からアクセスできる別々の変数となる
プロパティ
アクセサ
メンバ変数(インスタンス変数)に外部からアクセスすつ為のメソッド
get(set)アクセサ、getter(setter)と呼ばれる。
$p->SetMyVal("aaa");
$val = $p->GetMyVal
$val:aaa
class MyClass{
private $_myVal;
function SetMyVal($prm)
{
$this->_myVal = $prm;
}
function GetMyVal()
{
return $this->_myVal;
}
}
p.set(“aaa”)
print(p.get())
->aaa
class MyClass:
def get():
print(self._myVal)
def set(prm):
self._myVal = prm
private int _member1;
public int member1
{
//getアクセサ
get { return this._member1; }
//setアクセサ
valueは宣言不要
set { _member1 = value; }
}
//↑の省略形
public int member2 { get; set; }
public void SubRoutin1(){
~
}
}
public void main()
{
clsParent myParent = new clsParent();
myParent.member1 = 5;
int x = myParent.member1;
myParent.member2 = 10;
int y = myParent.member2;
myParent.SubRoutin1();
}
{
private:
int _member;
public:
int GetMember();
void SetMember(int member);
};
int MyClass::GetMember()
{
return this->_member;
}
void MyClass::SetMember(int member)
{
this->_member = member;
}
int main(array<System::String ^> ^args)
{
MyClass *myClass = new MyClass();
myClass->SetMember(10);
Console::WriteLine(myClass->GetMember());
// →10
delete myClass;
return 0;
}
※Getter/Setterを同名でオーバーロードすると呼び出し側からは使いやすくなる。
「オーバーロード」参照
Dim _Menber As Integer
Public Property Menber As Integer
getアクセサ
Get
Return _Member
End Get
setアクセサ
Set(ByVal prm As String)
_Member = prm
End Set
End Property
Public Sub SubRoutine()
~
End Sub
End Class
Dim myTest As New clsTest()
myTest.Member = 10
Dim x As Integer = myTest.Member
Call myTest.SubRoutine()
※MyClass.cls
Dim pMyVal As Integer
Dim pMyObj As Object
参照(読み取り)専用:データ型
Public Property Get Getter() As Integer
Getter = pMyVal
End Property
参照(読み取り)専用:オブジェクト型
Public Property Get Getter() As Object
Set Getter = pMyVal
End Property
設定(書き込み)専用:データ型
Public Property Let Letter(ByVal prm As Integer)
pMyVal = prm
※正確には↓ の様に書くが「Let」は省略可能。
Let pMyVal = prm
End Property
設定(書き込み)専用:オブジェクト型
Public Property Set Setter(ByVal prm As Object)
Set pMyObj = prm
End Property
イニシャライザー
Private Sub Class_Initialize()
Call MsgBox("Initialize()")
End Sub
ファイナライザー
Private Sub Class_Terminate()
Call MsgBox("Terminate()")
End Sub
※呼び出し元
Private Sub Form_Load()
Dim myClass As myClass
Set myClass = New myClass
myClass.Letter = 5
Let myClass.Letter = 5
Dim myObj As Object
Set myClass.Setter = myObj
Debug.Print (myClass.Getter)
Set myClass = Nothing
End Sub
private int _member1;
public int member1
{
//getter
get { return this._member1; }
//setter
valueは宣言不要
set { _member1 = value; }
}
public void SubRoutin1(){
~
}
}
public void main()
{
clsParent myParent = new clsParent();
myParent.member1 = 5;
int x = myParent.member1;
myParent.member2 = 10;
int y = myParent.member2;
myParent.SubRoutin1();
}
this.member1 = prm1;
this.member2 = prm2;
}
var number = new Calc1(1, 2);
var x = number.member1 + number.member2;
⇒ x : 3
number.member1 = 10;
number.member2 = 20;
var y = number.member1 + number.member2;
⇒ x : 30
var Calc2 = function(prm1, prm2){
this.member1 = prm1;
this.member2 = prm2;
};
var number = new Calc2(100, 200);
var x = number.member1 + number.member2;
⇒ x : 300
number.member1 = 1000;
number.number2 = 2000;
var y = number.member1 + number.member2;
⇒ y : 3000
読み取り・書き込み専用属性
int _member;
public int member{ get; }
int _member;
public int member{ set;}
宣言時、コンストラクタでのみ設定可能なインスタンス変数
(固定値(const)と違い、インスタンス毎に値を変えられる)
public class Test
{
private readonly int member = 5;
public Test(int init)
{
this.member = init;
}
public int Member
{
get { return this.member; }
⇒ OK
set { this.member = value; }
⇒ ビルドエラー
}
}
Public ReadOnly Property Menber As Integer
Get
Return _Member
End Get
End Property
Dim _Menber As Integer
Public WriteOnly Property Menber As Integer
Set(ByVal prm As String)
_Member = prm
End Set
End Property
インデクサ
※インスタンス自身をプロパティとして扱う
public class MyList<T>
{
T[] _list = new T[1000];
public T this[int index]
{
get { return _list[index]; }
set { _list[index] = value; }
}
}
public void Form1_Load(object sender, EventArgs e)
{
MyList<int> myList = new MyList<int>();
for (var i = 0; i < 10; i++)
{
myList[i] = i;
※インスタンスをプロパティとして扱っている
}
}
Public Class MyList(Of T)
Dim _list(1000) As T
//デフォルトのプロパティを宣言
Default Public Property list(ByVal index As Integer) As T
Get
Return _list(index)
End Get
Set(value As T)
_list(index) = value
End Set
End Property
End Class
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim myList As New MyList(Of Integer)
For i As Integer = 0 To 10 Step 1
myList(i) = i
※インスタンスをプロパティとして扱っている
Next i
End Sub
メソッド
メソッド種類
メソッド種類 | インスタンスからのアクセス | クラスからのアクセス |
---|---|---|
インスタンスメソッド | ◯ | |
スタティックメソッド | ◯ | ◯ |
クラスメソッド | ◯ | ◯ |
言語毎のメソッド有無
言語 | インスタンスメソッド | スタティックメソッド | クラスメソッド |
---|---|---|---|
Java | ◯ | ◯ | |
PHP | ◯ | ◯ | |
Python | ◯ | ◯ | ◯ |
スタティックメソッド、クラスメソッドの違い
言語によっては同じだったり片方が存在しなかったりする
pythonの場合
クラスに依存しないので単なるサブルーチンとして名前空間を分ける為に使用する
クラス内サブルーチン
public宣言する事でクラス外部から呼ばせる事が可能。
{
private int member;
public void SubRoutine(int prm1, int prm2)
{ this.member = prm1 + prm2; }
}
public void main()
{
Parent p = new Parent();
p.SubRoutine(prm1:5, prm2:10);
}
class MyClass
{
private:
int ~;
public:
void sayMember(){
~
}
};
// 外部宣言
class MyClass
{
private:
int ~;
public:
// プロトタイプ宣言
void sayMember();
};
void MyClass::sayMember()
{
~;
}
int main(array<System::String ^> ^args)
{
インスタンスをスタック領域に作成
MyClass myClass
myClass.sayMember();
インスタンスをヒープ領域に作成
MyClass *myClass = new MyClass();
myClass->sayMember();
delete myClass
return 0;
}
※スタック領域/ヒープ領域については、「メモリ管理/ヒープ領域」参照
Private member As Integer
Public Sub SubRoutine(
ByVal prm1 As Integer, _
ByVal prm2 As Integer)
me.member = prm1 + prm2
End Sub
End Class
Public Sub Main()
Dim p As New Parent()
p.SubRoutine(prm1:=5, prm2:=10)
End Sub
this.answer;
this.add = function(prm1, prm2){
this.answer = prm1 + prm2;
};
};
$p->Method();
aaa
class MyClass{
function Method(){
print("aaa");
}
}
オーバーロード
多重定義
引数・戻り値の異なる同名のメソッドを定義する事。
似た機能を同名にする事で直感的に使いやすくなる。
設計時には名前衝突も起こり難い。
{
public int member{ get; set; }
public void SubRoutine(int prm1, int prm2)
{ this.member = prm1 + prm2; }
//オーバーロード(引数が違う)
public void SubRoutine()
{ Console.WriteLine(this.member); }
//オーバーロード(戻り値が違う)
※引数も変える必要あり
public int SubRoutine(int prm1, int prm2, int prm3)
{
return prm1 + prm2 + prm3;
}
}
※Getter/Setterを同名でオーバーロードした例
class MyClass
{
private:
int _member;
public:
int Member();
void Member(int member);
};
int MyClass::Member()
{
return this->_member;
}
void MyClass::Member(int member)
{
this->_member = member;
}
int main(array<System::String ^> ^args)
{
MyClass *myClass = new MyClass();
myClass->Member(10);
Console::WriteLine(myClass->Member());
// →10
delete myClass;
return 0;
}
Private member As Integer
Public Sub SubRoutine(
ByVal prm1 As Integer, _
ByVal prm2 As Integer)
me.member = prm1 + prm2
End Sub
オーバーロード(引数が違う)
Public Overloads Sub SubRoutine()
Console.WriteLine(me.member)
End Sub
オーバーロード(引数が違う)
※引数も変える必要あり
Public Overloads Function SubRoutine(
ByVal prm1 As Integer, _
ByVal prm2 As Integer, _
ByVal prm3 As Integer) As Integer
Return prm1 + prm2 + prm3
End Sub
End Class
{
private int member;
public void SubRoutine(int prm1, int prm2)
{ this.member = prm1 + prm2; }
//オーバーロード(引数が違う)
public void SubRoutine()
{ Console.WriteLine(this.member); }
//オーバーロード(戻り値が違う)
※引数も変える必要あり
public int SubRoutine(int prm1, int prm2, int prm3)
{
return prm1 + prm2 + prm3;
}
}
this.answer;
this.add = function(prm1, prm2){
this.answer = prm1 + prm2;
};
};
var Child = function(){};
//継承
Child.prototype = new Parent();
Child.prototype.minus = function(prm){
this.answer -= prm;
};
var child = new Child();
child.add(1,1);
child.minus(1);
⇒this.answer = 1
演算子のオーバーロード
{
int x, y;
public MyClass() { x = y = 0; }
public MyClass(int i, int j) { x = i; y = j; }
public static MyClass operator +(MyClass prm1, MyClass prm2)
{
MyClass result = new MyClass();
result.x = prm1.x + prm2.x;
result.y = prm1.y + prm2.y;
return result;
}
}
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass(1, 1);
MyClass b = new MyClass(10, 10);
MyClass c = new MyClass();
c = a + b;
c.x:11 c.y:11
+はMyClassとMyClassの値が足されている
}
}
class MyClass
{
int x, y;
public MyClass() { x = y = 0; }
public MyClass(int i, int j) { x = i; y = j; }
public static MyClass operator +(MyClass prmObj, int prmInt)
{
MyClass result = new MyClass();
result.x = prmObj.x + prmInt;
result.y = prmObj.y + prmInt;
return result;
}
}
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass(1, 1);
MyClass result = new MyClass();
result = a + 5;
result.x:6 result.y:6
+はMyClassとintの値が足されている
}
}
{
private:
int myInt1;
int myInt2;
public:
MyClass(int prm1, int prm2);
void Show();
MyClass operator+(MyClass obj);
};
MyClass::MyClass(int prm1 = 0, int prm2 = 0)
{
this->myInt1 = prm1;
this->myInt2 = prm2;
};
void MyClass::Show()
{
cout << "myInt1:" << this->myInt1 << endl;
cout << "myInt2:" << this->myInt2 << endl;
}
MyClass MyClass::operator+(MyClass obj)
{
MyClass m;
m.myInt1 = this->myInt1 + obj.myInt1;
b.myInt1 + c.myInt2
m.myInt2 = this->myInt2 + obj.myInt2;
b.myInt2 + c.myInt2
return m;
結果をa.myInt1、a.myInt2に格納
インスタンス:mはここで破棄される
}
int _tmain(int argc, _TCHAR* argv[])
{
MyClass a(0, 0);
MyClass b(10, 20);
MyClass c(50, 60);
a = b + c;
オーバーロードした+を利用
bの+関数へcを渡し、結果をaに格納する
a = b.+(c)
a.Show();
→myInt1:60 myInt2:80
return 0;
}
クラス外にオーバーロード演算子を定義
パターン1
class MyClass
{
private:
int myInt;
public:
MyClass(int prm){ this->myInt = prm; };
int Getter(){ return this->myInt; }
};
通常の+演算子をオーバーロード
MyClass operator+(int prmI, MyClass prmM)
{
return MyClass(prmI + prmM.Getter());
}
int _tmain(int argc, _TCHAR* argv[])
{
MyClass a(0);
MyClass b(100);
a = 1000 + b;
オーバーロード+に引数1000とbインスタンスを渡し、結果をaに格納
(なぜか)左側数値が第一引数、右側オブジェクトが第二引数
→a.myInt:1100
return 0;
}
パターン2
MyClass operator+(MyClass m1, MyClass m2)
{
return MyClass(m1.Getter() + m2.Getter());
}
int _tmain(int argc, _TCHAR* argv[])
{
MyClass a(0);
MyClass b(100);
MyClass c(100);
a = b + c;
オーバーロード+に引数:aインスタンスとbインスタンスを渡し、結果をaに格納
(なぜか)左側数値が第一引数、右側オブジェクトが第二引数
→a.myInt:200
return 0;
}
メンバーの追加(JS)
var MyClass = function(){};
MyClass = function(){
this.member1;
};
MyClass = function(){
this.add1 = function(){
this.member1 += 1;
};
};
var myclass = new MyClass();
myclass.member1 = 5;
myclass.add1();
// ⇒ myclass.member1 = 6;
//インスタンスに対してメンバー追加
member.Minus = function(){
return this.member1 – this.member2;
};
number.Minus();
⇒-1000;
var newMember = new Calc2();
newMember.Minus();
//↑ 未定義エラー
インスタンス作成時のメソッドのコピー
全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
その為、例えばWebシステム用言語JavaScriptではprototype等を用いてメソッドのインスタンスへのコピーを省略する仕様を持つ。
しかしC#ではメソッドはインスタンスにコピーされず、クラスを参照する為のポインタがコピーされるのみ。
クラスのコピー(インスタンス作成)の際の、メソッドに関するメモリ制御を考慮する必要はない。
※プロパティはインスタンス毎に異なる値を持たせる意味はあるが、
全く同じ挙動をするメソッドを全インスタンスにコピーするのはメモリの無駄。
ブラウザの読み込み毎にインスタンスを作成するWEBシステムの特性を考慮し、
メソッドについては通常、以下の機能を用いてメモリーコピーを行わせない。
MyClass.prototype.plus10 = function(number){
this.member1 += 10;
this.member2 += 10;
}
var myclass = new MyClass()
myclass.plus10()
//プロトタイプ定義の別の書き方
MyClass.prototype = {
this.plus10 = function(number){
this.member1 += 10;
this.member2 += 10;
}
}