2013-02-21 73 views
15

考慮下面的PHP接口:你可以用不同但「兼容」的簽名覆蓋接口方法嗎?

interface Item { 
    // some methods here 
} 

interface SuperItem extends Item { 
    // some extra methods here, not defined in Item 
} 

interface Collection { 
    public function add(Item $item); 
    // more methods here 
} 

interface SuperCollection extends Collection { 
    public function add(SuperItem $item); 
    // more methods here that "override" the Collection methods like "add()" does 
} 

我使用PHPStorm,我這樣做的時候,我得到的,基本上國在SuperCollectionadd()定義的IDE的錯誤是不與定義兼容它延伸的接口,Collection

以某種方式,我可以看到這是一個問題,因爲該方法的簽名不匹配它「覆蓋」的那個,正好是。不過,我確實認爲這是兼容的,因爲SuperItem延伸了Item,所以我會查看add(SuperItem)add(Item)相同。

我很好奇這是否被PHP(版本5.4或更高版本)支持,也可能是IDE有一個錯誤,無法正確地捕捉到這個問題。

+1

在5.3,我碰到一個「致命錯誤:SuperCollection聲明:: Add()方法必須與採集的兼容::加入( )」。所以它看起來像PhpStorm是正確的。你爲什麼認爲5.4可能會有不同的表現? – halfer 2013-02-21 20:37:06

回答

10

不,我很確定PHP不支持這一點,在任何版本中,它寧可擊敗接口點。

接口的一點是,它爲您提供與其他引用相同接口的代碼的固定合同。

例如,考慮這樣的函數:

function doSomething(Collection $loopMe) { ..... } 

此功能期望接收一個實現Collection接口的對象。

在該函數中,程序員將能夠編寫對Collection中定義的方法的調用,知道該對象將實現這些方法。

如果你有這樣一個覆蓋的接口,那麼你有這個問題,因爲一個SuperCollection對象可以傳遞到函數中。它會起作用,因爲它的繼承性也是一個Collection對象。但是,函數中的代碼不能確定它知道方法的定義是什麼。

根據定義,接口是固定合同。它是不可變的。

作爲替代,您可以考慮使用抽象類而不是接口。這將允許您在非嚴格模式下進行覆蓋,但出於相同的原因,如果您使用嚴格模式,仍然會出現錯誤。

+0

感謝您的解釋!我在想,這應該在我的腦海中起作用,但是你幫助我看到了這個錯誤。 – jzimmerman2011 2013-02-21 20:58:03

+5

我不同意這個答案。接口不關心方法定義或其中的關係(例如'add()'項目意味着你也可以'delete()'它)。他們唯一的責任是執行兼容的方法簽名(輸入/輸出)。你是正確的,他們是固定合同,但對於合同的目的不正確。兩個類可以非常容易地實現相同的接口,並且具有*完全不同的語義(例如'add()'實際上可以*刪除一個類中的項目,只要輸入/輸出保持相同類型,它就無關緊要) 。 – FtDRbwLXw6 2013-02-21 21:37:29

+1

@drrcknlsn儘管我不認爲它使解釋失效,但是要記住這一點。 – qrazi 2013-08-16 06:55:01

2

的問題不是在IDE中。在PHP中,你不能重寫一個方法。並且兼容性只是在相反的方向上 - 您可以安全地期望父類的實例並接收子類。但是,當你期望一個子類時,如果你接收到父類,你就無法安全 - 子類可能定義父類中不存在的方法。但是,仍然無法覆蓋該方法

+8

請儘量使用正確的詞語。你可以在PHP中重寫一個方法,但不要超載它。 – 2013-12-20 11:14:33

0

當我有一種方法,我可能需要重載(PHP不支持),我確保方法參數之一(通常是最後一個)是一個數組。這樣我就可以通過我需要的任何東西。然後,我可以在函數中測試各種數組元素,告訴我需要執行的方法中的例程,通常在選擇/情況下。

2

作爲解決方法,我在接口中使用PHPDoc塊。

interface Collection { 
    /** 
    * @param Item $item 
    */ 
    public function add($item); 
    // more methods here 
} 

interface SuperCollection extends Collection { 
    /** 
    * @param SuperItem $item 
    */ 
    public function add($item); 
    // more methods here that "override" the Collection methods like "add()" does 
} 

這樣,如果您正確使用接口,IDE應該會幫助您捕獲一些錯誤。您也可以使用similar technique來覆蓋返回值類型。

0

擴展接口不允許更改方法定義。如果您的SuperItem擴展Item,則應該通過實現Collection接口的類傳遞,而不會出現問題。

但基於你真正想做的事,你可以嘗試:

  • 與SuperItem略有不同的方法創建接口和實施:

    interface SuperCollection extends Collection { 
        public function addSuper(SuperItem $superItem); 
    } 
    
  • 使用Decorator模式創建幾乎相同的接口不擴展:

    interface Collection { 
        public function add(Item $item); 
        // more methods here 
    } 
    
    interface SuperCollection { 
        public function add(SuperItem $item); 
        // more methods here that "override" the Collection methods like "add()" does 
    } 
    

    ñ裝飾(抽象)類,它將使用這個接口:

    class BasicCollection implements Collection { 
        public function add(Item $item) 
        { 
        } 
    } 
    
    class DecoratingBasicCollection implements SuperCollection { 
        protected $collection; 
    
        public function __construct(Collection $collection) 
        { 
         $this->collection = $collection; 
        } 
    
        public function add(SuperItem $item) 
        { 
         $this->collection->add($item); 
        } 
    } 
    
相關問題