タイプヒンティングとは?
「関数」もしくは「メソッド」が受け取る引数がオブジェクトである場合、定義時の引数の型に「インターフェイス」「クラス」を予め指定しておくことで、指定以外のオブジェクトを受け取らないようにすることができる。指定以外のインスタンスを受け取った場合には、エラーとなる。
↓
指定できる型は、「インターフェイス」「クラス」「配列」のみ。「string」「int」などの型指定はできない。
メリット
・クラス設計者はインターフェイスの仕様にもとづいてメソッドが作成できる
・クラス使用者はメソッド実行の際の引数に何を渡せばよいかを調べる必要がない
↓
クラス設計者の意図がクラス使用者に伝わりやすくなる
▼結果
/demo/type_hinting_01.html
▼結果
/demo/object_argument.html
・引数に「基底クラスオブジェクト」を指定している場合、基底クラスの特性を全て継承して備えているから「派生クラスオブジェクト」を渡してもO.K.。
・引数に「派生クラスオブジェクト」を指定している場合、基底クラスは派生クラスで追加された特性を備えていないため、「基底クラスオブジェクト」を渡すと、エラーとなる。
▼結果
/demo/object_argument2.html
「関数」もしくは「メソッド」が受け取る引数がオブジェクトである場合、定義時の引数の型に「インターフェイス」「クラス」を予め指定しておくことで、指定以外のオブジェクトを受け取らないようにすることができる。指定以外のインスタンスを受け取った場合には、エラーとなる。
↓
指定できる型は、「インターフェイス」「クラス」「配列」のみ。「string」「int」などの型指定はできない。
メリット
・クラス設計者はインターフェイスの仕様にもとづいてメソッドが作成できる
・クラス使用者はメソッド実行の際の引数に何を渡せばよいかを調べる必要がない
↓
クラス設計者の意図がクラス使用者に伝わりやすくなる
interface IProduct{
public function applyPriceDown();
public function getPrice();
public function setPrice($price);//利用しない
}
class DiscountDVD implements IProduct{// ディスカウントDVDクラス
private $price = 1000;//価格は、1,000円固定
public function applyPriceDown(){
$this->price = $this->price * 0.7;// 常に3割引
}
public function getPrice(){
return $this->price;
}
public function setPrice($price){//利用しない
}
}
class MonthlyDVD implements IProduct{// マンスリーDVDクラス
private $price = 1000;//価格は、1,000円固定
public function applyPriceDown(){
if ('29' == date('d')) {// 29日なら半額
$this->price = $this->price / 2;
}
}
public function getPrice(){
return $this->price;
}
public function setPrice($price){//利用しない
}
}
class DVD implements IProduct{// DVDクラス
private $price = 1000;//価格は、1,000円固定
public function applyPriceDown(){// 値引きなし
}
public function getPrice(){
return $this->price;
}
public function setPrice($price){//利用しない
}
}
class Cart{//カートクラスを定義
private $products = array();// 商品インスタンス保持用配列
public function addProduct(IProduct $product){//商品追加用のメソッド(引数は、商品クラスのインスタンス)。タイプヒンティングにより、「IProductを実装するクラスのインスタンス」を指定
$product->applyPriceDown();//このインスタンスのapplyPriceDownメソッドを実行して値引きの適用を行う
$this->products[] = $product;//プロパティである配列に追加
}
public function getTotalPrice(){//カート内商品の合計価格を取得するメソッド
$total = 0;
foreach ($this->products as $product) {//productsプロパティに格納されている全ての商品に対して
$total += $product->getPrice();//getPriceメソッドによって価格を取得し、合計している
}
return $total;
}
}
$cart = new Cart();// カート
// 商品
$discountdvd = new DiscountDVD();
$monthlydvd = new MonthlyDVD();
$dvd = new DVD();
// カートに商品追加
$cart->addProduct($discountdvd);
$cart->addProduct($monthlydvd);
$cart->addProduct($dvd);
$total = $cart->getTotalPrice();// 現在の合計価格を取得
var_dump($total);//結果、DiscountDVDが三割引きされ、700(DiscountDVD) + 1000(MonthlyDVD) + 1000(DVD) = 2700 という結果になる。もし、29日に実行すれば、700 + 500 + 1000 = 2200 という結果になる。if ('29' == date('d')) 部分を今日の日付へ変更しても、2200という結果を得られる。
▼結果
/demo/type_hinting_01.html
class AIRPORT{}
class NARITA extends AIRPORT{}
class HANEDA extends AIRPORT{}
class YOKOHAMA{}
function airport_func(AIRPORT $obj){//②⑥「引数$obj」の左側に「AIRPORT」を付与することにより、「引数$obj」に渡されるべき引数が「AIRPORT」クラスのオブジェクトに限定される。この様に定義された関数は、コール時に、指定されたクラス以外のクラスのオブジェクト、もしくはオブジェクト型以外の型の値が渡された時に「E_ERROR」エラーを発生させる。これによって、その関数の使用法が限定され、その目的・役目が明確になるというメリットがある。
echo '渡された$obj は、 \'AIRPORT\'クラスの instance でなければならない。', "\n";
echo '受取った$obj は、 \'', get_class($obj), "'クラスの instance です。\n\n";//get_class( )…引数で指定したオブジェクト変数が参照しているクラス名を参照
}
function narita_func(NARITA $obj){//④「引数$obj」に渡されるべき引数を「NARITA」クラスのオブジェクトに限定している
echo '渡された$obj は、 \'NARITA\'クラスの instance でなければならない。', "\n";
echo '受取った$obj は、 \'', get_class($obj), "'クラスの instance です\n\n";
}
#指定されたクラスのオブジェクトを渡す
airport_func(new AIRPORT);//①
narita_func(new NARITA);//③
#指定されたクラスの派生クラスのオブジェクトを渡す。派生クラスだから、O.K.
airport_func(new NARITA);//⑤
airport_func(new HANEDA);
#指定されたクラスの基底クラスのオブジェクトを渡すと、Catchable fatal error
/*
narita_func(new AIRPORT);//Catchable fatal error: Argument 1 passed to narita_func() must be an instance of NARITA, instance of AIRPORT given
*/
#指定されたクラスの兄弟クラスのオブジェクトを渡すと、Catchable fatal error
/*
narita_func(new HANEDA);//Catchable fatal error: Argument 1 passed to narita_func() must be an instance of NARITA, instance of HANEDA given
*/
#指定されたクラスと無関係なクラスのオブジェクトを渡すと、Catchable fatal error
/*
airport_func(new YOKOHAMA);
Catchable fatal error://Argument 1 passed to airport_func() must be an instance of AIRPORT, instance of YOKOHAMA given */
▼結果
/demo/object_argument.html
・引数に「基底クラスオブジェクト」を指定している場合、基底クラスの特性を全て継承して備えているから「派生クラスオブジェクト」を渡してもO.K.。
・引数に「派生クラスオブジェクト」を指定している場合、基底クラスは派生クラスで追加された特性を備えていないため、「基底クラスオブジェクト」を渡すと、エラーとなる。
class A{
public function punch($what){//④⑧punchメソッド
echo "{$what}クラスのpunch実行\n";
}
}
class B extends A{
public function kick(){//⑩kickメソッド
echo "kick実行\n";
}
}
function a_func(A $obj){//②a_func関数 ⑫BはAの派生クラスだからO.K.
echo "→→→ a_func() 開始 →→→\n";
$obj->punch("A");//③「A」クラスのオブジェクトが持つメソッドをコール
echo "←←← a_func() 終了 ←←←\n\n";
}
function b_func(B $obj){//⑥b_func関数
echo "→→→ b_func() 開始 →→→\n";
$obj->punch("B");//⑦「B」クラスの(基底クラスの)オブジェクトが持つメソッドをコール
$obj->kick();//⑨「B」クラスのオブジェクトが持つメソッドをコール
echo "←←← b_func() 終了 ←←←\n\n";
}
a_func(new A);//①
b_func(new B);//⑤
a_func(new B);//⑪
/*
b_func(new A);//Catchable fatal error: Argument 1 passed to b_func() must be an instance of B, instance of A given
*/
▼結果
/demo/object_argument2.html