インターフェイスとは?
状態:-
閲覧数:6,366
投稿日:2010-05-29
更新日:2013-04-18
・「インターフェイス」と「抽象メソッド」のみ定義可能なクラス
※インターフェイス定数 … クラス定数の様なもの
定義できるメソッドが抽象メソッドのみなので(通常メソッドは定義できない、クラスメソッド定義はできる)、「abstract修飾子」は要省略(書くとエラー)。
また、可視性は特性上常に「パブリック」なため、アクセス修飾子には「public」しか指定できない。
直接インスタンスを生成できないため、継承利用するが、インターフェイスの場合、継承とは呼ばず、「実装」と呼ぶ。 → 「インターフェイス」は、クラスを“継承”するのではなく“実装”する。
“実装”は「extends」ではなく「implements」を利用。
複数「インターフェイス」も指定できる。
実装したクラスは、全てのメソッドをオーバーライド&実装しなくてはならない。また、引数の数も名前も完全に一致している必要がある。
▼結果(定義しただけなので、出力結果なし)
/demo/interface_01.html
インターフェイスが、「メソッド存在を保証する役目」を果たしている例。
カートクラスで定義している二つのメソッド(addProduct、getTotalPrice)は、商品クラスインスタンスに二つのメソッド(applyPriceDown、getPrice)が定義されている前提で成り立っている。
↓
商品クラスには、これらのメソッド(applyPriceDown、getPrice)が確実に存在することが保証されていなければならない(存在しなければ、エラーとなるため)
↓
商品クラスがインターフェイス(この場合はIProduct)を実装していれば、そのインターフェイスに定義されているメソッドは100%存在することになる。
▼結果
/demo/interface_02.html
「インターフェイス」とそれを実装したクラスは、継承関係にあるわけではないので、インターフェイス定数を「parent::I1_CONST」で参照することはできない。
▼結果
/demo/interface1.html
「インターフェイス」は「extends」を使って、他の「インターフェイス」を継承して拡張することができる。また、その場合は、「,(コンマ)」で区切って複数指定できる。
▼結果
/demo/interface2_extends.html
「インターフェイスを実装したクラス」を継承したクラスも、「インターフェイスを継承したインターフェイス」を実装したクラスも、そのクラスは「継承元のインターフェイスを実装」していると見做される。
▼結果
/demo/interface3.html
オブジェクト引数のインターフェイス指定
「関数」や「クラスのメンバ関数」の定義時には、「オブジェクト引数のクラス指定」だけではなく「オブジェクト引数のインターフェイス指定」もできる。この場合、渡されたオブジェクトが、指定された「インターフェイス」を実装しているか否かがチェックされる。
▼結果
/demo/interface4_object.html
定義済みのインターフェイス
PHPには予め幾つかの「インターフェイス」が定義されている。
定義済みの「インターフェイス」を確認してみる。
▼結果
/demo/interface5_already.html
※インターフェイス定数 … クラス定数の様なもの
定義できるメソッドが抽象メソッドのみなので(通常メソッドは定義できない、クラスメソッド定義はできる)、「abstract修飾子」は要省略(書くとエラー)。
また、可視性は特性上常に「パブリック」なため、アクセス修飾子には「public」しか指定できない。
直接インスタンスを生成できないため、継承利用するが、インターフェイスの場合、継承とは呼ばず、「実装」と呼ぶ。 → 「インターフェイス」は、クラスを“継承”するのではなく“実装”する。
“実装”は「extends」ではなく「implements」を利用。
複数「インターフェイス」も指定できる。
実装したクラスは、全てのメソッドをオーバーライド&実装しなくてはならない。また、引数の数も名前も完全に一致している必要がある。
interface IProduct{
public function applyPriceDown();
public function getPrice();
public function setPrice($price);
}
class Product implements IProduct{
public function applyPriceDown(){
// 処理を記述
}
public function getPrice(){
// 処理を記述
}
public function setPrice($price){
// 処理を記述
}
}
▼結果(定義しただけなので、出力結果なし)
/demo/interface_01.html
インターフェイスが、「メソッド存在を保証する役目」を果たしている例。
カートクラスで定義している二つのメソッド(addProduct、getTotalPrice)は、商品クラスインスタンスに二つのメソッド(applyPriceDown、getPrice)が定義されている前提で成り立っている。
↓
商品クラスには、これらのメソッド(applyPriceDown、getPrice)が確実に存在することが保証されていなければならない(存在しなければ、エラーとなるため)
↓
商品クラスがインターフェイス(この場合はIProduct)を実装していれば、そのインターフェイスに定義されているメソッドは100%存在することになる。
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($product){//商品追加用のメソッド(引数は、商品クラスのインスタンス)
$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/interface_02.html
「インターフェイス」とそれを実装したクラスは、継承関係にあるわけではないので、インターフェイス定数を「parent::I1_CONST」で参照することはできない。
interface I1{//「I1」「インターフェイス」を定義
//protected $property; //「インターフェイス」ではプロパティを定義できない ⇒ Fatal error: Interfaces may not include member variables
const I1_CONST = "I1インターフェイス定数";
//abstract public function m1();//「インターフェイス」では、「abstract修飾子」は要省略 ⇒ Fatal error: Access type for interface method I1::m1() must be omitted
public function m1();//定義出来るメソッドが抽象メソッドのみなので、「abstract修飾子」は要省略
//abstract protected function protected_method();//「パブリック」以外の可視性は不可)⇒ Fatal error: Access type for interface method I1::protected_method() must be omitted
//public function not_interface_method(){}//「インターフェイス」では、通常のメソッド定義(処理部を記述)はできない)⇒ Fatal error: Interface function I1::not_interface_method() cannot contain body
}
interface I2{
function m2();
function m3($arg1, $arg2 = "");
static function m4();
}
#クラスは、全てのメソッドをオーバーライドして実装しなくてはならない ⇒ Fatal error: Class C0 contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (I2::m3, I2::m4)
//class C0 implements I2{
// public function m2(){}
//}
class C1{
const C1_CONST = "C1クラス定数";
}
#「複数インターフェイス実装」 + 「クラス継承」
class C2 extends C1 implements I1, I2{
//const I1_CONST = "インターフェイス定数1";//継承することはできません ⇒ 「インターフェイス定数」は「クラス定数」として再定義できない ⇒ Fatal error: Cannot inherit previously-inherited constant I1_CONST from interface I1
const C1_CONST = "C2で再定義した「C1クラス定数」";
public function __construct(){
echo "self::C1_CONST : ", self::C1_CONST, "\n";
echo "parent::C1_CONST : ", parent::C1_CONST, "\n";
echo "C1::C1_CONST : ", C1::C1_CONST, "\n";
echo "self::I1_CONST : ", self::I1_CONST, "\n";
echo "parent::I1_CONST : "/*, parent::I1_CONST*/, "\n";//未定義のクラス定数 ⇒ Fatal error: Undefined class constant 'I1_CONST'
echo "I1::I1_CONST : ", I1::I1_CONST, "\n";
}
public function m1(){
//
}
public function m2(){
//
}
public function m3($a1, $a2 = "air"){
//
}
static public function m4(){
//
}
private function air_method(){
//
}
}
new C2;
▼結果
/demo/interface1.html
「インターフェイス」は「extends」を使って、他の「インターフェイス」を継承して拡張することができる。また、その場合は、「,(コンマ)」で区切って複数指定できる。
interface I1{
const I1_CONST = "インターフェイス1定数";
function m1();
}
interface I2{
const I2_CONST = "インターフェイス2定数";
function m2();
}
#「インターフェイス」を継承
interface I3 extends I1, I2{
// const I1_CONST = "ダミー"; //「インターフェイス定数」再定義は不可 ⇒ Fatal error: Cannot inherit previously-inherited constant I1_CONST from interface I1
}
#「インターフェイス」実装
class I4 implements I1, I2{
public function m1(){
//
}
public function m2(){
//
}
}
#「インターフェイス」実装
class C1 implements I3{
public function m1(){
//
}
public function m2(){
//
}
}
▼結果
/demo/interface2_extends.html
「インターフェイスを実装したクラス」を継承したクラスも、「インターフェイスを継承したインターフェイス」を実装したクラスも、そのクラスは「継承元のインターフェイスを実装」していると見做される。
#「インターフェイス」の定義
interface I1{
//
}
interface I2 extends I1{//インターフェイスを継承したインターフェイス
//
}
#「インターフェイス」の実装
abstract class C1Base implements I1{//インターフェイスを実装したクラス
//
}
class C2 extends C1Base{//「インターフェイスを実装したクラス」を継承したクラス
//
}
class C3 implements I2{//「インターフェイスを継承したインターフェイス」を実装したクラス
//
}
#オブジェクトが実装する「インターフェイス」を調べる
$imp_arr["C2] = class_implements(new C2);//「class_implements()」関数 ⇒ 「指定したオブジェクトが実装するインターフェイス名を配列に格納して返す」
$imp_arr["C3] = class_implements(new C3);
print_r($imp_arr);
▼結果
/demo/interface3.html
オブジェクト引数のインターフェイス指定
「関数」や「クラスのメンバ関数」の定義時には、「オブジェクト引数のクラス指定」だけではなく「オブジェクト引数のインターフェイス指定」もできる。この場合、渡されたオブジェクトが、指定された「インターフェイス」を実装しているか否かがチェックされる。
interface I1{}
interface I2{}
interface I3 extends I1, I2{}//複数インターフェイスを継承したインターフェイス
interface I4{}
abstract class C1Base implements I3{}//インターフェイスを実装したクラス
class C1 extends C1Base{}//「インターフェイスを実装したクラス」を継承したクラス
function f1(C1Base $obj){//②
echo '$obj は、\'C1Base\'のインスタンスじゃなきゃダメ。', "\n";
echo '$obj は、\'', get_class($obj), "'のインスタンス。\n\n";
}
function f2(I1 $obj){//④
echo '$obj は、\'I1\'インターフェイスの実装じゃなきゃダメ。', "\n";
echo '$obj implements...', "\n";
print_r(class_implements($obj));
}
function f3(I4 $obj){
echo '$obj must implement interface \'I4\'.', "\n";
echo '$obj implements...', "\n";
print_r(class_implements($obj));
}
f1(new C1);//①
f2(new C1);//③
//f3(new C1); //インターフェイス「I4」を実装していなければならない ⇒ Catchable fatal error: Argument 1 passed to f3() must implement interface I4, instance of C1 given, called
▼結果
/demo/interface4_object.html
定義済みのインターフェイス
PHPには予め幾つかの「インターフェイス」が定義されている。
定義済みの「インターフェイス」を確認してみる。
#定義済み「インターフェイス」と定義されるメソッド名を取得・出力
foreach(get_declared_interfaces() as $interface_name)//get_declared_interfaces ? 宣言されている全てのインターフェースの配列を返す
$interfaces[$interface_name] = get_class_methods($interface_name);//get_class_methods ? クラスメソッドの名前を取得
print_r($interfaces);
echo "
";
#定義済み「インターフェイス」を実装する定義済みクラス名を抽出・出力
foreach(get_declared_classes() as $class_name){
$buf_arr = array();
$class_abj = new ReflectionClass($class_name);
$imp_arr = $class_abj->getInterfaces();
foreach($imp_arr as $imp_obj){
$imp_arr_ = get_object_vars($imp_obj);
$buf_arr[] = $imp_arr_["name];
}
if(count($buf_arr))
$arr[$class_name] = $buf_arr;
}
print_r($arr);
▼結果
/demo/interface5_already.html