学习+面试 - 抽象类和接口的区别和项目中的应用

抽象类

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

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。 这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。

抽象类内未必有抽象方法,但有抽象方法的类,则必是抽象类

Example #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

Example #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 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

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

需要注意的是,在接口中定义一个构造方法是被允许的。在有些场景下这可能会很有用,例如用于工厂模式时。

 

接口本身就是抽象的,但注意不是抽象类,因为接口不是类,只是其方法是抽象的

实现(implements

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

Note:

在 PHP 5.3.9 之前,实现多个接口时,接口中的方法不能有重名,因为这可能会有歧义。在最近的 PHP 版本中,只要这些重名的方法签名相同,这种行为就是允许的。

Note:

接口也可以继承,通过使用 extends 操作符。

Note:

类要实现接口,必须使用和接口中所定义的方法完全一致的方式。否则会导致致命错误。

常量

接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。

范例

Example #1 接口示例

<?php

// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 实现接口
// 下面的写法是正确的
class Template implements iTemplate
{
    private $vars = array();
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

// 下面的写法是错误的,会报错,因为没有实现 getHtml():
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
    private $vars = array();
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
}
?>

Example #2 可扩充的接口

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

interface b extends a
{
    public function baz(Baz $baz);
}

 


// 正确写法
class c implements b
{
    public function foo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// 错误写法会导致一个致命错误
class d implements b
{
    public function foo()
    {
    }

    public function baz(Foo $foo)
    {
    }
}
?>

Example #3 继承多个接口

<?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()
    {
    }
}
?>

Example #4 使用接口常量

<?php
interface a
{
    const b = 'Interface constant';
}

// 输出接口常量
echo a::b;

// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
    const b = 'Class constant';
}
?>

 

接口加上类型约束,提供了一种很好的方式来确保某个对象包含有某些方法。参见 instanceof 操作符和类型约束

接口和抽象类的区别:

1、接口用implements实现,可以像抽象类接口一样用extends继承;

2、抽象类可以有属性(变量等)、普通方法、抽象方法,但接口不能有属性、普通方法、但可以有常量;

3、一个类可以继承多个接口(interface c extentds a,b{}),而一个类只能继承一个抽象类(class a extends b{})

4、抽象类用abstract关键字在类前声明,且有class声明为类,接口是用interface来声明,但不能用class来声明,因为接口不是类;

5、抽象类的抽象方法一定要用abstract来声明,而接口则不需要;

6、接口中的方法和实现它的类默认都是public类型的,抽象类中的方法可以使用private,protected,public来修饰;

7、接口没有构造函数,抽象类可以有构造函数(待考证,因为官网文档里说明:”需要注意的是,在接口中定义一个构造方法是被允许的。在有些场景下这可能会很有用,例如用于工厂模式时。“)

 

接口和抽象类的使用:

如果要创建一个模型,这个模型将由一些紧密相关的对象采用,就可以使用抽象类。如果要创建将由一些不相关对象采用的功能,就使用接口。
如果必须从多个来源继承行为,就使用接口。
如果知道所有类都会共享一个公共的行为实现,就使用抽象类,并在其中实现该行为

 

Final类/方法


(1)final类不能被继承
(2)final方法不能被重写

Static类/方法


(1)可以不实例化类而直接访问
(2)静态属性不可以由对象通过->操作符来访问,用::方式调用

<?php

# 接口

interface Human{

    const TEST_CONST = "test const"; // 定义常量

    // public $v; // error,不能定义变量

    // static $count; // error,不能定义变量

    public function speak();

    public function walk();

    public function run();

}

      

# 抽象类

abstract class Father implements Human{

      

    public function construct(){

        echo "father init n";

    }

      

    abstract public function walk(); // 抽象方法

      

    public function speak(){

        echo "father speak skill n";

    }

      

    public function run(){

        echo "father run skill n";

    }

}

      

# 非抽象类

class Mother implements Human{

      

    public function construct(){

        echo "mother init n";

    }

      

    # 这里必须实现walk方法

    public function walk(){

        echo "mother walk skill n"; 

    } 

      

    public function speak(){

        echo "mother speak skill n";

    }

      

    public function run(){

        echo "mother run skill n";

    }

}

      

class Son extends Father{

      

    public function walk(){

        echo "son walk skill. n";

    }

      

    public function speak($name = ''){

        echo "son: ". $name ." speak skill. n";

    }

      

    # 访问控制必须和父类中一样(或者更为宽松)

    protected function sport(){

        echo "son sport skill. n";

    }

      

    final public function notTeach(){

        echo 'son has not to teach skill';

    }

}

      

class Daughter extends Mother{

      

    public function run($name = ''){

        echo "daughter run skill. n";

    }

      

}

      

final class GrandChild extends Son{

          

    # 访问控制必须和父类中一样(或者更为宽松)

    public function sport(){

        echo "GrandChild sport skill. n";

    }

      

    # Cannot override final method Son::notTeach()

    // public function notTeach(){} // error

}

      

#  Class Orphan may not inherit from final class (GrandChild)

// class Orphan extends GrandChild{}  // error

      

$son = new Son();

$son->speak("Suly");

      

$daughter = new Daughter();

$daughter->run('Lily');

      

$grandChild = new GrandChild();

$grandChild->sport();

 

posted @ 2020-04-23 17:18  joker_one  阅读(295)  评论(0编辑  收藏  举报