接口允许您创建定义实现接口的类的方法的代码。但是,您不能向这些方法添加任何代码。

抽象类允许您做同样的事情,同时向方法添加代码。

现在,如果可以用抽象类实现同样的目标,我们为什么还需要接口的概念呢?

有人告诉我,这与从c++到Java的面向对象理论有关,这也是PHP面向对象的基础。这个概念在Java中有用而在PHP中没有吗?这仅仅是一种避免在抽象类中布满占位符的方法吗?我遗漏了什么吗?


当前回答

接口的存在不是作为类可以扩展的基础,而是作为所需函数的映射。

下面是一个不适合抽象类的接口的例子: 假设我有一个日历应用程序,它允许用户从外部源导入日历数据。我将编写类来处理导入每种类型的数据源(ical, rss, atom, json),每个类都将实现一个公共接口,以确保它们都具有我的应用程序获取数据所需的公共方法。

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Then when a user adds a new feed I can identify the type of feed it is and use the class developed for that type to import the data. Each class written to import data for a specific feed would have completely different code, there may otherwise be very few similarities between the classes outside of the fact that they are required to implement the interface that allows my application to consume them. If I were to use an abstract class, I could very easily ignore the fact that I have not overridden the getEvents() method which would then break my application in this instance whereas using an interface would not let my app run if ANY of the methods defined in the interface do not exist in the class that implemented it. My app doesn't have to care what class it uses to get data from a feed, only that the methods it needs to get that data are present.

To take this a step further, the interface proves to be extremely useful when I come back to my calendar app with the intent of adding another feed type. Using the ImportableFeed interface means I can continue adding more classes that import different feed types by simply adding new classes that implement this interface. This allows me to add tons of functionality without having to add unnecessarily bulk to my core application since my core application only relies on there being the public methods available that the interface requires so as long as my new feed import classes implement the ImportableFeed interface then I know I can just drop it in place and keep moving.

这只是一个非常简单的开始。然后,我可以创建另一个接口,所有日历类都需要实现该接口,该接口提供了更多特定于类处理的提要类型的功能。另一个很好的例子是验证提要类型的方法,等等。

This goes beyond the question but since I used the example above: Interfaces come with their own set of issues if used in this manner. I find myself needing to ensure the output that is returned from the methods implemented to match the interface and to achieve this I use an IDE that reads PHPDoc blocks and add the return type as a type hint in a PHPDoc block of the interface which will then translate to the concrete class that implements it. My classes that consume the data output from the classes that implement this interface will then at the very least know it's expecting an array returned in this example:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

没有太多空间来比较抽象类和接口。接口是简单的映射,在实现时要求类具有一组公共接口。

其他回答

接口的全部意义在于为您提供灵活性,使您的类强制实现多个接口,但仍然不允许多重继承。从多个类继承的问题很多,维基百科的页面很好地总结了这些问题。

接口是一种折衷。关于多重继承的大多数问题并不适用于抽象基类,所以现在大多数现代语言禁用多重继承,但却调用抽象基类接口,并允许一个类“实现”任意数量的抽象基类。

这个概念在面向对象编程中非常有用。对我来说,我认为接口是一种契约。只要我的类和你的类同意这个方法签名合同,我们就可以“接口”。至于抽象类,我认为更多的是存根一些方法的基类,我需要填充细节。

接口的存在不是作为类可以扩展的基础,而是作为所需函数的映射。

下面是一个不适合抽象类的接口的例子: 假设我有一个日历应用程序,它允许用户从外部源导入日历数据。我将编写类来处理导入每种类型的数据源(ical, rss, atom, json),每个类都将实现一个公共接口,以确保它们都具有我的应用程序获取数据所需的公共方法。

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Then when a user adds a new feed I can identify the type of feed it is and use the class developed for that type to import the data. Each class written to import data for a specific feed would have completely different code, there may otherwise be very few similarities between the classes outside of the fact that they are required to implement the interface that allows my application to consume them. If I were to use an abstract class, I could very easily ignore the fact that I have not overridden the getEvents() method which would then break my application in this instance whereas using an interface would not let my app run if ANY of the methods defined in the interface do not exist in the class that implemented it. My app doesn't have to care what class it uses to get data from a feed, only that the methods it needs to get that data are present.

To take this a step further, the interface proves to be extremely useful when I come back to my calendar app with the intent of adding another feed type. Using the ImportableFeed interface means I can continue adding more classes that import different feed types by simply adding new classes that implement this interface. This allows me to add tons of functionality without having to add unnecessarily bulk to my core application since my core application only relies on there being the public methods available that the interface requires so as long as my new feed import classes implement the ImportableFeed interface then I know I can just drop it in place and keep moving.

这只是一个非常简单的开始。然后,我可以创建另一个接口,所有日历类都需要实现该接口,该接口提供了更多特定于类处理的提要类型的功能。另一个很好的例子是验证提要类型的方法,等等。

This goes beyond the question but since I used the example above: Interfaces come with their own set of issues if used in this manner. I find myself needing to ensure the output that is returned from the methods implemented to match the interface and to achieve this I use an IDE that reads PHPDoc blocks and add the return type as a type hint in a PHPDoc block of the interface which will then translate to the concrete class that implements it. My classes that consume the data output from the classes that implement this interface will then at the very least know it's expecting an array returned in this example:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

没有太多空间来比较抽象类和接口。接口是简单的映射,在实现时要求类具有一组公共接口。

在我看来,接口应该优于非函数抽象类。如果性能受到影响,我也不会感到惊讶,因为只实例化了一个对象,而不是解析两个对象,并将它们组合在一起(尽管,我不能确定,我不熟悉OOP PHP的内部工作原理)。

的确,与Java相比,接口没有那么有用/有意义。另一方面,PHP6将引入更多的类型提示,包括返回值的类型提示。这将为PHP接口增加一些价值。

interfaces定义了需要遵循的方法列表(想想API),而抽象类提供了一些基本/通用的功能,这些功能由子类细化以满足特定需求。

我不记得PHP在这方面是否有所不同,但在Java中,您可以实现多个接口,但不能继承多个抽象类。我认为PHP也是这样工作的。

在PHP中,您可以通过用逗号分隔多个接口来应用它们(我认为,我不认为这是一个干净的解决方案)。

至于多个抽象类,你可以有多个相互扩展的抽象(同样,我不完全确定,但我想我以前在某个地方见过)。唯一不能扩展的是final类。