<?php declare(strict_types=1); ini_set('display_errors', 'On'); error_reporting(-1); /** * 接口Ability1 */ interface Ability1 { public const ABILITY1_NAME = '技能1'; // 接口不能定义变量,但能定义常量 public function dig(): void; } /** * 接口Ability2 */ interface Ability2 { public const ABILITY2_NAME = '技能2'; // 接口不能定义变量,但能定义常量 public function climb(): void; } /** * 兔子类(只实现Ability1接口) */ class Rabbit implements Ability1 { public function dig(): void { echo '兔子打洞' . PHP_EOL; } } /** * 猴子类(只实现Ability2接口) */ class Monkey implements Ability2 { public function climb(): void { echo '猴子爬树' . PHP_EOL; } } /** * 鼯鼠类(同时实现Ability1接口和Ability2接口) */ class Rodentia implements Ability1, Ability2 { public function dig(): void { echo '鼯鼠打洞' . PHP_EOL; } public function climb(): void { echo '鼯鼠爬树' . PHP_EOL; } } echo Ability1::ABILITY1_NAME . PHP_EOL; // 技能1 echo Ability2::ABILITY2_NAME . PHP_EOL; // 技能2 $rabbit = new Rabbit(); $rabbit->dig(); // 兔子打洞 $monkey = new Monkey(); $monkey->climb(); // 猴子爬树 $rodentia = new Rodentia(); $rodentia->dig(); // 鼯鼠打洞 $rodentia->climb(); // 鼯鼠爬树 //========== 总结 ==========// // 1、接口里定义的方法都必须是public,且不能写方法体(即便是空方法体{}也不行),这是接口的特性。 // 2、接口可以继承接口,并且和类继承类一样都是用extends关键字,类在实现子接口时也必须实现父接口的方法。 // 3、类如果要实现接口则必须实现该接口定义的全部方法,只要有一个方法未实现都会报“Fatal error”。 // 4、接口不能包含成员变量(属性),但能包含常量(使用const关键字定义,但不建议在接口里定义常量),并且不同接口可以包含同名常量, // 接口常量使用格式为“接口名::常量名”,如:Ability1::ABILITY1_NAME, // 5、和大多数面向对象编程语言一样,PHP也是不支持多继承,但可以实现多个接口,格式为“class 类名 implements 接口1, 接口2, 接口3”。 // 需要注意的是实现多个接口时接口之间不能有同名常量,否则会报“Fatal error”。
<?php declare(strict_types=1); ini_set('display_errors', 'On'); error_reporting(-1); /** * 打印变量的相关信息 * * @param mixed $value 要打印的表达式 * @param mixed ...$values 更多要打印的表达式 * @return void echo */ function v(mixed $value, mixed ...$values): void { ob_start(); // 打开输出控制缓冲 var_dump($value); echo ob_get_clean(); // 从缓冲区获取var_dump()的内容,然后清空缓冲区 foreach ($values as $v) { v($v); // 递归 } } /** * Ability1接口 */ interface Ability1 { public function swim(): void; } /** * Ability2接口 */ interface Ability2 { public function drive(): void; } /** * Ability3接口 */ interface Ability3 { public function program(): void; } /** * Human类 */ class Human implements Ability1, Ability2 { public string $name; public function __construct(string $name) { $this->name = $name; } public function swim(): void { echo "俺({$this->name})学会游泳辣~~~"; } public function drive(): void { echo "俺({$this->name})学会开车辣~~~"; } } $human = new Human('张三'); v($human::class); // string(5) "Human" v(gettype($human)); // string(6) "object" v(get_class($human)); // string(5) "Human" v($human instanceof Human); // bool(true) v($human instanceof Ability1); // bool(true) v($human instanceof Ability2); // bool(true) v($human instanceof Ability3); // bool(false) v($human instanceof PDO); // bool(false) //========== 总结 ==========// // 1、接口虽然不能实例化,但是也能用instanceof关键字判断一个类实例是否实现了接口。
<?php declare(strict_types=1); ini_set('display_errors', 'On'); error_reporting(-1); /** * 打印变量的相关信息 * * @param mixed $value 要打印的表达式 * @param mixed ...$values 更多要打印的表达式 * @return void echo */ function v(mixed $value, mixed ...$values): void { ob_start(); // 打开输出控制缓冲 var_dump($value); echo ob_get_clean(); // 从缓冲区获取var_dump()的内容,然后清空缓冲区 foreach ($values as $v) { v($v); // 递归 } } /** * 测试将函数的形参和返回值都声明为接口类型 * * @param Ability $ability Ability接口(重要说明:形参允许声明为接口类型) * @return Ability Ability接口(重要说明:返回值允许声明为接口类型) */ function foo(Ability $ability): Ability { // 重要说明:输入“$ability->”的时候,PhpStorm只会提示Ability接口的三个方法,而不会提示Human类的name属性, // 必须使用instanceof进行判断,确保$ability是Human类实例PhpStorm才会提示name属性。 if ($ability instanceof Human) { echo "[函数]俺叫$ability->name~~~" . PHP_EOL; } return $ability; } /** * Ability接口 */ interface Ability { public function swim(): void; public function drive(): void; public function program(): void; } /** * Human类 */ class Human implements Ability { public string $name; public function __construct(string $name) { $this->name = $name; } public function swim(): void { echo "俺({$this->name})学会游泳辣~~~" . PHP_EOL; } public function drive(): void { echo "俺({$this->name})学会开车辣~~~" . PHP_EOL; } public function program(): void { echo "俺({$this->name})学会编程辣~~~" . PHP_EOL; } public function intro(): void { echo "[方法]俺叫$this->name~~~" . PHP_EOL; } } $human = new Human('张三'); // 重要说明:虽然foo()的形参要求数据类型为Ability接口,但由于Human类实现了Ability接口,所以可以用Human类实例入参 $ability = foo($human); // [函数]俺叫张三~~~ v($ability::class); // string(5) "Human" v(gettype($ability)); // string(6) "object" v(get_class($ability)); // string(5) "Human" // 重要说明:$ability既是Human类实例,又是Ability接口实例,两者并不冲突 v($ability instanceof Human); // bool(true) v($ability instanceof Ability); // bool(true) $ability->swim(); // 俺(张三)学会游泳辣~~~ $ability->drive(); // 俺(张三)学会开车辣~~~ $ability->program(); // 俺(张三)学会编程辣~~~ $ability->intro(); // [方法]俺叫张三~~~ //========== 总结 ==========// // 1、函数(或方法)的形参可以声明为接口类型,并且实参必须是实现了该接口的类的实例。 // 2、函数(或方法)的返回值可以声明为接口类型,并且实际返回值必须是实现了该接口的类的实例。 // 3、无论是形参还是返回值,如果声明数据类型为接口,使用时需要特别小心,因为已经丢失了接口实现类的具体信息,也就是说开发者只知道变量 // 是某个实现了接口的类的实例,却不知道到底是什么类(即类名),所以也就无法使用类属性和类方法,如果想使用类属性和类方法就必须先使 // 用instanceof关键字确定是什么类(有点类似反射Reflection,都是运行时分析)。 // 4、正如上面所说,接口类型变量会丢失接口实现类的具体信息,开发者或许明确知道$ability变量是Human类实例,但对于PhpStorm来说并不知道, // 所以在使用$ability变量时PhpStorm只会提示Ability接口的三个方法,而无法提示Human类的name属性,只有在使用instanceof关键字确定了 // $ability变量是Human类实例才会提示name属性。 // 5、接口类型变量的本质始终是类实例,只是裹了一层接口的外衣,就像张三穿上马甲戴上面具也还是那个张三,只是没那么容易被认出来而已。
Copyright © 2024 码农人生. All Rights Reserved