一、抽象类
定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须声明为抽象的,被定义为抽象的方法只是生命了其调用方式(参数),不能定义其具体的功能。
继承一个抽象类的时候子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为轻松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者共有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如:子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。
实例
<?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,则不能继承。