js 调用另一个类的方法_一个隐藏在方法集和方法调用中且易被忽略的小细节
作為一個(gè)長(zhǎng)期從事Go語(yǔ)言開(kāi)發(fā)的程序猿,筆者不敢說(shuō)自己是老油條但也勉強(qiáng)算一個(gè)小油條。然而就在今天,筆者研究TLS/SSL握手源碼的時(shí)候,突然靈光一閃,想到了一個(gè)和自己認(rèn)知不符的現(xiàn)象,于是趕緊寫(xiě)了一個(gè)例子驗(yàn)證一番,結(jié)果當(dāng)頭一棒直到碼這篇文章時(shí)依舊懵逼。
話不多說(shuō),上錘!
不好意思,不是這個(gè)錘,是下面這個(gè):
type set interface {set1(s string)set2(s string) } type test struct {s string } func (t *test) set1(s string) {t.s = s } func (t test) set2(s string) {t.s = s } func main() {var (t1 testt2 = new(test))t1.set1("1")fmt.Print(t1.s)t1.set2("2")fmt.Print(t1.s)t2.set1("3")fmt.Print(t2.s)t2.set2("4")fmt.Print(t2.s)fmt.Print(" ")_, ok1 := (interface{}(t1)).(set)_, ok2 := (interface{}(t2)).(set)fmt.Println(ok1, ok2) }正確答案筆者就不直接公布了,請(qǐng)各位讀者耐心在后文尋找答案。
方法集
根據(jù)golang官方文檔知道,一個(gè)類型有一個(gè)與之關(guān)聯(lián)的方法集。接口類型的方法集是接口中定義的方法。
官方文檔中特別提到,類型T的方法集包含用T聲明為Receiver的所有方法,而指針類型*T的方法集包含用T和*T聲明的所有方法。
此時(shí),我們回到上面的例子可以很明顯的知道下面這段代碼輸出為false true。
_, ok1 := (interface{}(t1)).(set) _, ok2 := (interface{}(t2)).(set) fmt.Println(ok1, ok2)T類型的方法集不包含*T類型的方法集,因此t1無(wú)法轉(zhuǎn)為set接口類型。
事實(shí)上,根據(jù)這部分官方文檔筆者更加疑惑了,因?yàn)樯鲜隼涌梢哉_\(yùn)行,而且類型為test的變量調(diào)用了(*test).set1方法。抱著這樣的疑惑筆者瘋狂谷狗,最后在stackoverflow的指導(dǎo)下發(fā)現(xiàn)了這種情況和方法調(diào)用有關(guān)。
這里特別感謝一下谷狗和stackoverflow。
方法調(diào)用
方法調(diào)用筆者在這里僅說(shuō)明和本篇相關(guān)的內(nèi)容,其他細(xì)節(jié)相信各位讀者都已經(jīng)了然于胸。
下面,先看看官方文檔原文:
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()簡(jiǎn)單來(lái)說(shuō),如果x可尋址,且&x的方法集包含m,則x.m()是(&x).m的縮寫(xiě)。這樣前面的例子能夠正常運(yùn)行也在情理之中了。
因此,前面例子的最終輸出結(jié)果是:1133 false true。
如果讀者對(duì)結(jié)構(gòu)體中的值未發(fā)生改變有疑惑,請(qǐng)參考筆者的這篇文章——為什么go中的receiver name不推薦使用this或者self。
你以為你都懂了
寫(xiě)完前面的方法集和方法調(diào)用筆者細(xì)細(xì)思考一番,確認(rèn)沒(méi)有其他遺漏的細(xì)節(jié),于是放心的上了個(gè)廁所。結(jié)果廁所還沒(méi)上完,立馬想到一個(gè)問(wèn)題(額外多扯一句,筆者經(jīng)常在上廁所的時(shí)候找到靈感,這可能就是勞逸結(jié)合的最佳實(shí)踐吧):
type los stringfunc (s los) p1() {fmt.Println(s) }func (s *los) p2() {fmt.Println(s) }func main() {var s1 los = "1111"var s2 *los = &s1const s3 los = "3333"s1.p1()s1.p2()s2.p1()s2.p2()s3.p1()s3.p2() }如果你對(duì)上面的代碼沒(méi)有任何疑問(wèn)且認(rèn)為上述代碼能夠正常運(yùn)行,那只能說(shuō)明你對(duì)本文的閱讀還不夠認(rèn)真。
我們先看看上述代碼在vscode中的報(bào)錯(cuò)。
前面介紹方法調(diào)用時(shí),如果x可尋址,則x可以調(diào)用&x的類型的方法集。上述代碼s3是常量,是不可以尋址的,因此無(wú)法調(diào)用(*los).p2方法。
以上,就是筆者曾經(jīng)忽略的細(xì)節(jié),現(xiàn)在回過(guò)頭來(lái)看一看倒也充滿了樂(lè)趣。
彩蛋
本篇是研究TLS/SSL握手流程的副產(chǎn)品,因?yàn)門LS/SSL握手流程筆者還在整理中,故這篇文章先行一步給個(gè)預(yù)告,下一期TLS/SSL握手流程敬請(qǐng)期待。
最后,衷心希望本文能夠?qū)Ω魑蛔x者有一定的幫助。
注: 1. 寫(xiě)本文時(shí), 筆者所用go版本為: go1.15.2 2. 文章中所用完整例子:https://github.com/Isites/go-coder/blob/master/receiver/main.go參考
https://golang.org/ref/spec#Method_sets
https://golang.org/ref/spec#Calls
總結(jié)
以上是生活随笔為你收集整理的js 调用另一个类的方法_一个隐藏在方法集和方法调用中且易被忽略的小细节的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle的OCI目录下没有sampl
- 下一篇: android 重启app_[Boot]