博客列表 >抽象类,对象接口,匿名类,trait,final关键字

抽象类,对象接口,匿名类,trait,final关键字

Whitney的博客
Whitney的博客原创
2018年12月11日 11:04:072697浏览

一、抽象类

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

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

实例

<?php
abstract class AbstractClass{

    //强制要求子类中***定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);
    abstract protected function prefixName($name);

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

}

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

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

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

        return "{$prefix}{$separator}{$name}";
    }

}

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

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

    //若该方法不声明,则会报错
    public function prefixName($name){

    }

}

$class1 = new ConcreteClass1();
$class1->printOut(); //ConcreteClass1
echo $class1->prefixValue("F00_")."\n"; // F00_ConcreteClass1
echo $class1->prefixName("Pacman"), "\n"; //Mr.Pacman


$class2 = new ConcreteClass2();
$class2->printOut();//ConcreteClass2
echo $class2->prefixValue("F00_")."\n"; //F00_ConcreteClass2
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

二、对象接口

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

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

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

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

实现多个接口时,接口中的方法不能有重名。

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

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

实例

<?php
interface a{
    //接口常量不能被覆盖
    const param = 'Interface constant';
    public function foo();
}

interface b{
    public function bar();
}

interface c extends a,b{
    public function baz();
}
class d implements c{
    public function foo(){
        echo a::param;
    }

    public function bar(){

    }

    public function baz(){

    }
}

$d =new d();
$d->foo();//Interface constant

?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

三、匿名类

PHP 7 开始支持匿名类。匿名类很有用,可以创建一次性的简单对象。

可以传递参数到匿名类的构造器,也可以扩展(extend)其他类。实现接口(implement interface),以及像其他普通的类一样使用trait:

实例

<?php

class SomeClass{}
interface SomeInterface{}
trait SomeTrait{}

var_dump(new class(10) extends SomeClass implements SomeInterface{
    private $num;
    public function __construct($num){
        $this->num = $num;
    }
    
    use SomeTrait;
});
//object(class@anonymous)#1 (1) { ["num":"class@anonymous":private]=> int(10) }

?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

匿名类被嵌套进普通Class后,不能访问这个外部类(outer class)的private(私有)、protected(受保护)方法或属性。为了访问外部类(Outer class)protected属性或方法,匿名类可以extend(扩展)此外部类。为了使用外部类(Outer class)的private属性,必须通过构造器传进去:

实例

<?php
class Outer{
    private $prop = 1;
    protected $prop2 = 2;

    protected  function func1(){
        return 3;
    }

    public function func2(){
        return new class($this->prop) extends Outer{
            private $prop3;

            public function __construct($prop){
                $this->prop3 = $prop;
            }

            public function func3(){
                echo $this->prop2 ;//2
                 
                echo $this->prop3 ;//1
                 
                echo $this->func1();//3
                 
                echo $this->prop2 + $this->prop3 + $this->func1();//6
            }
        };
    }
}

(new Outer)->func2()->func3();
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

四、trait

1、trait是为类似PHP的单继承语言而准备的一种代码复用机制。trait为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用method。

trait和class类似,但仅仅旨在用细粒度和一致的方式来组合功能。无法通过trait自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个class之间不需要继承。

从基类继承的成员会被trait插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了trait的方法,而trait则覆盖了被继承的方法。

实例

<?php
class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o->sayHello();// Hello World!
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

2、多个trait

通过逗号分隔,在use声明列出多个trait,可以都插入到一个类中。

实例

<?php
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();//Hello
$o->sayWorld();//World
$o->sayExclamationMark();//!
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

3、冲突的解决

如果两个trait都插入了一个同名的方法,如果没有明确解决中途将会产生一个致命错误。

为了解决多个trait在通一个类中的命名冲突,需要使用insteadof操作符来明确指定使用冲突方法中的哪一个。

以上方式仅允许排除掉其它方法,as操作符可以为某个方法引入别名。注意,as操作符不会对方法进行重命名,也不会影响其方法。

实例

<?php
trait A{
    public function smallTalk(){
        echo "a";
    }

    public function bigTalk(){
        echo "A";
    }
}

trait B{
    public function smallTalk(){
        echo 'b';
    }

    public function bigTalk(){
        echo "B";
    }
}

class Talker{
    use A,B{
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker{
    use A,B{
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

4、修改方法的访问控制

使用as语法还可以用来调整方法的访问控制。

实例

<?php
trait HelloWorld{
    public function sayHello(){
        echo "Hello World!";
    }
}

//修改了sayhello的访问控制
class MyClass1{
    use HelloWorld{ sayHello as protected;}
}

// 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化

class MyClass2{
    use HelloWorld{ sayHello as private myPrivateHello;}
}
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

5、从trait来组成trait

实例

<?php
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

6、trait的抽象成员

实例

<?php
trait Hello {
    public function sayHelloWorld() {
        echo 'Hello'.$this->getWorld();
    }
    abstract public function getWorld();
}

class MyHelloWorld {
    private $world;
    use Hello;
    public function getWorld() {
        return $this->world;
    }
    public function setWorld($val) {
        $this->world = $val;
    }
}
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

7、trait的静态成员

实例

<?php
trait Counter {
    public function inc() {
        static $c = 0;
        $c = $c + 1;
        echo "$c\n";
    }
}

class C1 {
    use Counter;
}

class C2 {
    use Counter;
}

$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

实例

<?php
trait Counter {
    public function inc() {
        static $c = 0;
        $c = $c + 1;
        echo "$c\n";
    }
}

class C1 {
    use Counter;
}

class C2 {
    use Counter;
}

$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

实例

<?php
trait StaticExample {
    public static function doSomething() {
        return 'Doing something';
    }
}

class Example {
    use StaticExample;
}

Example::doSomething();
?>

运行实例 »

点击 "运行实例" 按钮查看在线实例

8、Trait 同样可以定义属性。

Trait 定义了一个属性后,类就不能定义同样名称的属性,否则会产生 fatal error。

五、final关键字

如果父类中的方法被声明为final,则子类无法覆盖该方法。如果一个类被声明为final,则不能继承。




声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议