抽象类和接口的区别

本文介绍了PHP中的抽象类和接口的概念及其使用。抽象类不能被实例化,至少包含一个抽象方法,子类必须实现所有抽象方法。接口定义了类必须实现的方法,但不提供实现。使用接口能确保不同类间的一致性和互换性,适用于多态场景。示例展示了如何定义和使用抽象类及接口,并强调了方法签名的兼容性规则。
摘要由CSDN通过智能技术生成

抽象类

定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的

抽象类示例1


<?php
abstract class AbstractClass
{
 // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
?>

以上程序会输出:

ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。但是,如果子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突
抽像类示例2

<?php
abstract class AbstractClass
{
    // 我们的抽象方法仅需要定义需要的参数
    abstract protected function prefixName($name);

}

class ConcreteClass extends AbstractClass
{

    // 我们的子类可以定义父类签名中不存在的可选参数
    public function prefixName($name, $separator = ".") {
        if ($name == "Pacman") {
            $prefix = "Mr";
        } elseif ($name == "Pacwoman") {
            $prefix = "Mrs";
        } else {
            $prefix = "";
        }
        return "{$prefix}{$separator} {$name}";
    }
}

$class = new ConcreteClass;
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";

以上程序会输出:

Mr. Pacman
Mrs. Pacwoman

对象接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。 由于接口(interface)和类(class)、trait 共享了命名空间,所以它们不能重名。

接口就像定义一个标准的类一样,通过 interface 关键字替换掉 class 关键字来定义,但其中所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

使用接口的目的

  • 因为实现了同一个接口,所以开发者创建的对象虽然源自不同的类,但可能可以交换使用。 常用于多个数据库的服务访问、多个支付网关、不同的缓存策略等。 可能不需要任何代码修改,就能切换不同的实现方式。
  • 能够让函数与方法接受一个符合接口的参数,而不需要关心对象如何做、如何实现。 这些接口常常命名成类似 Iterable、Cacheable、Renderable, 以便于体现出功能的含义。

接口的实现(implements)

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。 类可以实现多个接口,用逗号来分隔多个接口的名称。

  • 注意:类实现(implement)两个接口时,如果它们定义了相同名称的方法,只有签名相同的时候才是允许的

  • 注意:类实现接口时,必须以兼容的签名定义接口中所有方法。

签名兼容性规则

当覆盖(override)方法时,签名必须兼容父类方法。 否则会导致 Fatal 错误(PHP 8.0.0 之前是 E_WARNING 级错误)。 兼容签名是指:遵守协变与逆变规则; 强制参数可以改为可选参数;新参数为可选参数。 这就是著名的里氏替换原则(Liskov Substitution Principle),简称 LSP。 不过构造器和 私有(private)方法不需要遵循签名兼容规则, 哪怕签名不匹配也不会导致 Fatal 错误。

接口的扩展(extends)

接口也可以通过 extends 操作符扩展。 如下代码所示:

<?php
interface A
{
    public function foo();
}

interface B
{
    public function bar();
}

interface C extends A, B
{
    public function baz();
}

class D implements C
{
    public function foo()
    {
    }

    public function bar()
    {
    }

    public function baz()
    {
    }
}

参考文章

PHP官方手册-对象接口

PHP官方手册-抽象类

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值