整理思路時想到乙個問題:php為什麼不允許同名函式存在?即不允許常見於其他語言的過載機制?
過載和重寫
先區分一下過載(overload)和重寫(override):過載指多個名字相同,但引數不同的函式在同一作用域並存的現象;重寫出現在繼承中,指子類重定義父類功能的現象,也被稱為覆蓋。過載中說的引數不同有三種情況:引數個數不同,引數型別不同,引數順序不同。重寫一般指函式的覆蓋,即相同簽名的成員函式在子類中重新定義(實現抽象函式或介面不是重寫),是實現多型(polymorphism)的一種關鍵技術。成員變數也可以過載/覆蓋,但一般不會這麼做。
用簡單的c**來說明過載:int add(int a, int b)
double add(double a, double b)
double add(int a, int b, double c)
double add(double a, int b, int c)
第乙個函式為參考基準,其他三個對應過載的三種情形。函式過載多見於強型別語言,編譯後函式在函式符號表的名稱一般是函式名加引數型別。上面的四個函式,g++編譯後,nm命令檢視符號表中的名字,輸出如下:[tlanyan@server ~]# nm test | grep add
0000000000400730 t _global__sub_i__z3addii
0000000000400851 t _z3adddd
00000000004008b1 t _z3adddii
000000000040083d t _z3addii
000000000040087d t _z3addiid
最後四行的第三列對應編譯後四個函式的符號資訊,_z3為字首,add是函式名,剩下的字母d代表double,i代表int,與生命一一對應。
再回到php的過載。php的函式宣告中引數無需宣告型別,直接排除引數型別不同、引數順序不同兩種過載,只剩下引數個數不同一條路可走。定義乙個引數個數不同名字相同的函式,這麼乙個小小的過載要求,在php中也是不合法的!原因是php中不允許同名函式存在,想定義重名函式,死心吧!雖然大多數情況下以預設引數方式實現過載基本上夠用,但不時還會覺得憋屈,忍不住想問一句:php為什麼不允許(同名函式)過載啊?!
php的苦衷
php不支援同名函式的過載是有原因的。上面已經提到,php函式宣告時不需要指定引數型別,過載中的三種情況立馬廢掉兩種。倖存的引數個數不同這一條路也走不通,為什麼呢?因為php中呼叫函式時,少傳引數,不行;多傳引數,沒問題!來個簡單的例子:function foo($arg1, $arg2) {
echo "$arg1, $arg2\n";
// 函式呼叫
//php warning: missing argument 2 for foo()
// php notice: undefined variable: arg2 in php shell code on line 2
foo("tlanyan");
// 引數個數正好,執行正常
foo("hello", "tlanyan");
// 多傳引數,執行正常
foo("hello", "tlanyan", "nice day");
// 傳更多引數,也一切正常
foo("hello", "tlanyan", "morning", "noon", "afternoon", "evening", "night");
只要個數不小於宣告的,傳多少引數php不管。所以引數個數不同,在php中不足以區分函式。
個人認為另乙個不允許名函式存在的重要原因是function_exists、 method_exists、is_callable這些api的存在。作為簡單易用的語言,php為開發人員提供了查詢函式名是否存在/可用的便利api,這在程式語言中很少見(尤其是get_defined_functions這類api)。可以看到,這些api都不需要引數資訊。如果能定義引數不同的過載函式,這些api都要跟著改,勢必引入額外的複雜性。正所謂魚與熊掌不可兼得,方便你用時沒想到引數不同,不方便你定義就抱怨,好像不好吧?
php5引入了反射api,這是非常強大的型別資訊查詢工具。就函式宣告而言,reflectionmethod/reflectionfunction類的getparameters/getnumberofparameters/getnumberofrequiredparameters等api,功能上甩function_exists等好幾條街。有了反射機制,按理說function_exists這些api可以安心的退休。雖然反射這一套東西功能強大,但遠沒有舊式api簡單好用。再加上看看市面上的**,有多少類庫和框架依賴舊式api。從相容性和實用性考慮,個人認為短時間內能以同名函式方式過載的概率非常小。
php中的過載
只看完上面的內容就說php不支援過載,我想隨便乙個資深的php開發都會不由自主的取下拖鞋,然後教你什麼是php中的過載,並保證至少有好幾種實現方法;官方人員對這種認知估計也無力吐槽:能不能好好看官方文件?!官網中可是有一節專門講過載!
因為種種原因,php不支援傳統的過載,也就是同名函式的過載,但php是支援過載的,而且姿勢還不少。簡單來說,php中主要有以下幾種過載方式:預設引數,定義乙個全面的函式版本,不是必須的值在宣告時賦予預設值;
定義乙個不宣告引數的入口函式,函式內使用func_num_args/func_get_args獲取引數個數/陣列,然後根據引數個數**到具體實現的函式;
自php5.6起,可以用變長引數實現過載,func_get_args的另一種形式;
對於類中的成員函式,可以通過__call和__callstatic實現過載。
總結php的特性決定了其不支援同名函式方式的過載,但並不意味著php不支援過載。實際上php可以多種方式實現過載,並保持其一貫的簡單易用性。
感謝閱讀!
參考
php重寫與過載
過載 重寫和重構在之前就學習過,但到現在仍然不能準確的說出它們到底有什麼區別,該怎麼具體的應用,看來真是書到用時方恨少,這種狀況是不是特別嚴重?真的該總結回顧了。重寫 override 什麼是重寫 子類可繼承父類中的方法,而不需要重新編寫相同的方法。但有時子類並不想原封不動地繼承父類的方法,而是想作...
php之重寫與過載
1.重寫 覆蓋override 子類重寫了父類的同名方法的內容 例如 class human class stu extends human ming new stu ming say 張三 ming say 2.過載 overloading 存在多個同名的方法,但是引數型別 個數不同 傳不同的引數...
php方法過載方法重寫 php中重寫方法有什麼規則
php中重寫方法的規則有 1 final修飾的類方法不能被子類重寫 2 如果要重寫父類方法,那麼引數個數必須一致 3 在重寫時,訪問級別只可以等於或大於父類,不可提公升訪問級別。方法重寫規則 1 final修飾的類方法不可被子類重寫 final修飾的類方法不可被子類重寫 即便final privat...