2009-04-14 18 views
20

試圖找出PHP是否支持類似方法重載,繼承和多態的功能,我發現:PHP中的多態現象看起來真的是多態嗎?

  • 它不支持方法重載
  • 它支持繼承

,但我我不確定多態性。我發現這個谷歌搜索互聯網:

I should note that in PHP the polymorphism isn't quite the way it should be. I mean that it does work, but since we have a weak datatype, its not correct.

那麼它是否真的多態?

編輯 只是不能在PHP supports polymorphism旁邊確定是或否。我不願陳述:「PHP不支持多態」,實際上它確實如此。或相反亦然。

+5

」PHP支持多態,但有一些限制。「田田。 :) – 2009-04-15 01:10:36

+0

謝謝:) upvoted你的答案。 – 2009-04-15 09:43:08

+0

爲什麼這個問題的語言不可知?似乎完全是關於PHP的。 – beldaz 2011-01-09 22:38:08

回答

31
class Animal { 
    var $name; 
    function __construct($name) { 
     $this->name = $name; 
    } 
} 

class Dog extends Animal { 
    function speak() { 
     return "Woof, woof!"; 
    } 
} 

class Cat extends Animal { 
    function speak() { 
     return "Meow..."; 
    } 
} 

$animals = array(new Dog('Skip'), new Cat('Snowball')); 

foreach($animals as $animal) { 
    print $animal->name . " says: " . $animal->speak() . '<br>'; 
} 

你可以標記所有你想要的,但看起來像多態對我來說。

4

你仍然可以覆蓋方法,只是不超載它們。重載(使用C++)是多個方法使用相同方法名的地方,只有參數的數量和類型不同。這在PHP中很難,因爲它是弱類型的。

覆蓋是子類替換基類中的方法的地方。這是多態的基礎,你可以在PHP中做到這一點。

+0

抱歉,這不完全是我的意思。我明白爲什麼PHP不支持方法重載,以及重載和覆蓋之間的區別。但我能否宣稱它支持多態? – 2009-04-14 23:40:43

+0

通過給出一個錯覺,我可以在PHP中實現方法重載,正如我在答案中所說明的那樣。 – 2014-08-16 12:48:30

6

__call() and __callStatic()應該支持方法重載。更多信息,請登錄manual。或者你究竟在幹什麼?

更新:我剛剛注意到其他答覆。

另一種方式重載的方法,考慮以下因素:

<?php 
public function foo() 
{ 
    $args = func_get_arg(); 
} 

當然不是很漂亮,但它可以讓你做幾乎任何你想要的。

16

儘管PHP不支持方法重載您在其他語言中體驗過的方式,比如說Java。但是您可以在PHP中使用方法重載,但定義方法不同。 如果你想有一個給定的方法不同的功能,不同的組在PHP中的參數,你可以做這樣的事情:

class myClass { 
    public function overloadedMethod() { 
     // func_num_args() is a build-in function that returns an Integer. 
     // the number of parameters passed to the method. 
     if (func_num_args() > 1) { 
      $param1 = func_get_arg(0); 
      $param2 = func_get_arg(1); 
      $this->_overloadedMethodImplementation2($param1,$param2) 
     } else { 
      $param1 = func_get_arg(0); 
      $this->_overloadedMethodImplementation1($param1) 
     } 
    } 

    protected function _overloadedMethodImplementation1($param1) { 
     // code 1 
    } 

    protected function _overloadedMethodImplementation2($param1,$param2) { 
     // code 2 
    } 
} 

有可能是更清潔的實現,但是這僅僅是一個樣品。

PHP支持繼承和接口。所以你可以使用它們的多態性。你可以有一個這樣的接口:

// file: MyBackupInterface.php 
interface MyBackupInterface { 
    // saves the data on a reliable storage 
    public function saveData(); 
    public function setData(); 
} 

// file: myBackupAbstract.php 
require_once 'MyBackupInterface.php'; 
class MyBackupAbstract implements MyBackupInterface { 
    protected $_data; 
    public function setData($data) { 
     $this->_data= $data; 
    } 
    // there is no abstract modifier in PHP. so le'ts avoid this class to be used in other ways 
    public function __construct() { 
      throw new Exception('this class is abstract. you can not instantiate it'); 
    } 
} 

// file: BackupToDisk.php 
require_once 'MyBackupAbstract.php'; 
class BackupToDisk extends MyBackupAbstract { 
    protected $_savePath; 

    // implement other methods ... 

    public function saveData() { 
      // file_put_contents() is a built-in function to save a string into a file. 
      file_put_contents($this->_savePath, $this->_data); 
    } 
} 

// file: BackupToWebService.php 
require_once 'MyBackupAbstract.php'; 
class BackupToWebService extends MyBackupAbstract { 
    protected $_webService; 
    // implement other methods ... 

    public function saveData() { 
      // suppose sendData() is implemented in the class 
      $this->sendData($this->_data); 
    } 
} 
現在

在你的應用程序,你可能會使用這樣的:

// file: saveMyData.php 
// some code to populate $myData 
$backupSolutions = array(new BackupToDisk('/tmp/backup') , new BackupToWebService('webserviceURL')); 
foreach ($backupSolutions as $bs) { 
    $bs->setData($myData); 
    $bs->saveData(); 
} 

你是對的,PHP是不強類型語言,我們從來沒有提出過任何您的$ backupSolutions將是'MyBackupAbstract'或'MyBackupInterface',但是這不會阻止我們使用與使用相同方法不同的功能的多態性質。

1

對於我在這裏看到的PHP不支持多態,也不支持重載方法。你可以通過自己的方式來實際接近這兩種功能,但它們遠不是它的最初目的。這裏的許多例子要麼是擴展一個類,要麼創建一個詭計來諷刺多態。

2

多態性可以通過以下方法來實現:

  1. method overriding - 正常的漂亮是如上

  2. method overloading

你可以通過創建方法重載的錯覺魔法__call():

class Poly { 
    function __call($method, $arguments) { 
     if ($method == 'edit') { 
      if (count($arguments) == 1) { 

       return call_user_func_array(array($this,'edit1'), $arguments); 
      } else if (count($arguments) == 2) { 
       return call_user_func_array(array($this,'edit2'), $arguments); 
      } 
     } 
    } 
    function edit1($x) { 
     echo "edit with (1) parameter"; 
    } 

    function edit2($x, $y) { 
     echo "edit with (2) parameter"; 
    } 

} 

$profile = new Poly(); 
$profile->edit(1); 
$profile->edit(1,2); 

Expln:

1) Here we are utilizing the power of __call() of listening calls of 
    non-available  methods and 
2) after knowing it who had called with their inputs diverting them to desired 
    method 

In php,我們其實working under the hood得到所需的行爲,給人的感覺of method overloading

6

PHP有基於類的多態性,但缺乏實施的正式機制論據爲基礎的多態性。

基於類的多態性意味着你可以用基類來思考,並且調用的方法依賴於最終的類。例如,如果您有各種類的對象數組,例如Triangle和Circle,並且這些類中的每個類都擴展了相同的類Shape,則可以將您的數組視爲一組形狀。您可以遍歷形狀並調用每個形狀的getArea()方法。多態性是調用getArea()方法取決於對象的類的現象。如果你的形狀是一個三角形,則調用Triangle :: getArea(),如果一個圓形,然後Circle :: getArea()被調用 - 即使你的代碼不區分圓形和三角形,而是將每個對象視爲只是一個形狀。同一行代碼會導致執行不同的代碼塊,具體取決於對象的類別。

基於參數的多態性是一些強類型語言的一個特性,其中可以在一個類中定義多個同名的方法,只要它們具有不同的參數;那麼調用哪個方法取決於提供的參數。您可以通過在您的方法中手動考慮您的參數類型,在PHP等弱類型語言中模擬基於參數的多態性。這是jQuery爲了實現多態API而做的事情,儘管JavaScript缺乏基於本機參數的多態性。

因此,如果通過「支持多態性」,您的具體意思是它提供了一個實現基於參數的多態性的正式機制,答案是否定的。對於更廣泛的解釋,答案是肯定的。基於類的多態現象在每一種面向對象的語言中都存在;對於執行隱式類型轉換來實現基於參數的多態性的語言來說是沒有意義的。

3

PHP允許多態代碼在其他語言中產生編譯錯誤。一個簡單的例證說明第一C++代碼,其產生預期編譯錯誤:

class Base {}; 

class CommonDerivedBase { 
    public: 
    // The "= 0" makes the method and class abstract 
    // virtual means polymorphic method 
    virtual whoami() = 0; 
}; 

class DerivedBase : public CommonDerivedBase { 
    public:  
    void whoami() { cout << "I am DerivedBase \n"; } 
}; 

class Derived1 : public CommonDerivedBase { 
    public: 
    void whoami() { cout << "I am Derived1\n"; } 
}; 

class Derived2 : public CommonDerivedBase { 
public: 
void whoami() { cout << "I am Derived2\n"; } 
}; 

/* This will not compile */ 
void test_error(Base& db) 
{ 
    db.whoami(); 
} 

C++編譯器將發出此錯誤消息爲線db.whoami()

error: no member named 'whoami' in 'Base' 

因爲基地沒有一個方法叫做WHOAMI()。但是,類似的PHP代碼在運行時纔會發現這樣的錯誤。

class Base {} 

abstract class DerivedCommonBase { 
    abstract function whoami(); 
} 

class Derived1 extends DerivedCommonBase { 

public function whoami() { echo "I am Derived1\n"; } 
} 

class Derived2 extends DerivedCommonBase { 

public function whoami() { echo "I am Derived2\n"; } 
} 

/* In PHP, test(Base $b) does not give a runtime error, as long as the object 
* passed at run time derives from Base and implements whoami(). 
*/ 
function test(Base $b) 
{ 
    $b->whoami(); 
} 

$b = new Base(); 
$d1 = new Derived1(); 
$d2 = new Derived2(); 

$a = array(); 

$a[] = $d1; 
$a[] = $d2; 

foreach($a as $x) { 

    echo test($x); 
} 

test($d1); 
test($d2); 
test($b); //<-- A run time error will result. 

foreach循環的工作原理與輸出

I am Derived1 
I am Derived2 

直到你調用測試($ B),並通過基地的一個實例,將您得到一個運行時錯誤。所以的foreach後,輸出將是

I am Derived1 
I am Derived2 
PHP Fatal error: Call to undefined method Base::whoami() in 
home/kurt/public_html/spl/observer/test.php on line 22 

關於你可以做,使PHP的安全將是增加一個運行時檢查 測試如果$ b是類的一個實例的唯一的事情你打算。

function test(Base $b) 
{ 
    if ($b instanceof DerivedCommonBase) { 
    $b->whoami(); 
    } 
} 

但多態性的要點是消除這種運行時間檢查。 「