เหตุใดการกำหนดอินเทอร์เฟซจึงเข้มงวดกว่าการเรียกเมธอด
ฉันกำลังผ่าน Tour of Go ฉันได้เรียนรู้ว่าหากเรามีวิธีการที่ยอมรับตัวชี้เป็นตัวรับก็จะยอมรับประเภทค่าเป็นตัวรับด้วย (ดำเนินการแปลงโดยอัตโนมัติ)
type Vertex struct { X, Y float64 }
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X * v.X + v.Y * v.Y)
}
จากนั้นรหัสต่อไปนี้จะถูกต้องไม่ว่าจะได้รับ Vertex ด้วยค่าหรือตัวชี้
v := Vertex{1, 2}
fmt.Println(v.Abs())
p := &v
fmt.Println(p.Abs())
อย่างไรก็ตามสมมติว่าเรามีอินเทอร์เฟซต่อไปนี้:
type Abser interface {
Abs() float64
}
แล้วเหตุใดรหัสต่อไปนี้จึงไม่ถูกต้อง?
var a Abser
v := Vertex{1, 2}
a = v // invalid
ความเข้าใจของฉันคือมันจะดี แม้ว่า v จะเป็นค่าประเภทที่ "ใช้" ฟังก์ชัน Abs ซึ่งรับตัวรับตัวชี้ แต่ก็จะรับค่าด้วย?
อินเทอร์เฟซถูกออกแบบมาให้เข้มงวดมากขึ้นในแง่ของสิ่งที่ตัวแปรอินเทอร์เฟซสามารถเก็บไว้ทางด้านขวามือหรือไม่? อินเทอร์เฟซจะเห็น * Vertex และ Vertex เป็นสองประเภทที่แตกต่างกันอย่างไรก็ตามวิธีการ Abs () ไม่มีปัญหาในการประมวลผลเช่นกัน
คำตอบ
ในทั้งสองกรณี Go ต้องการตัวชี้เพื่อเรียกใช้เมธอด ความแตกต่างที่สำคัญคือการเรียกเมธอดจะรับที่อยู่โดยอัตโนมัติv
แต่ตรวจสอบว่ามีบางสิ่งที่ใช้อินเทอร์เฟซไม่ได้หรือไม่
วิธีการโทร:
เมื่อเรียกเมธอดด้วยตัวรับตัวชี้ในประเภทธรรมดา Go จะรับที่อยู่โดยอัตโนมัติหากได้รับอนุญาต จากข้อมูลจำเพาะเกี่ยวกับการเรียกวิธีการ (ไฮไลต์เป็นของฉัน):
การเรียกเมธอด xm () ใช้ได้ถ้าชุดวิธีของ (ประเภทของ) x มี m และรายการอาร์กิวเมนต์สามารถกำหนดให้กับรายการพารามิเตอร์ของ m ถ้า x สามารถระบุแอดเดรสได้และชุดวิธีของ & x มี m, xm () คือชวเลขสำหรับ (& x) .m ()
ในกรณีนี้x
หมายถึงตัวแปรของคุณv
และสามารถระบุแอดเดรสได้ (&v).Abs()
ดังนั้นวิธีการในการโทรไปทำงานโดยอัตโนมัติ
การมอบหมาย:
เมื่อพยายามที่จะกำหนด ให้ตรวจสอบที่จะต้องเป็นa = v
fullfilled ดำเนินการเฉพาะเมื่อชุดวิธีการตรงกับอินเทอร์เฟซ ชุดวิธีนี้ถูกกำหนดดังนี้:T is an interface type and x implements T.
v
Abser
ชุดวิธีการประเภทอื่น ๆ T ประกอบด้วยวิธีการทั้งหมดที่มีการประกาศรับ T ชุดวิธีการของชนิดตัวชี้ที่เกี่ยวข้อง * T คือชุดของวิธีการทั้งหมดที่ประกาศด้วยตัวรับ * T หรือ T (นั่นคือชุดวิธีการของ T)
คุณจะสังเกตเห็นว่าเมื่อคำนวณชุดวิธีการ Go จะไม่ใช้ที่อยู่ของv
เหมือนที่ทำในการเรียกใช้เมธอด ซึ่งหมายความว่าเมธอดที่ตั้งไว้var v Vertex
ว่างเปล่าไม่สามารถใช้งานอินเทอร์เฟซได้
สารละลาย:
วิธีแก้ปัญหานี้คือใช้ที่อยู่ของv
คุณเอง:
var a Abser
v := Vertex{1, 2}
a = &v
โดยการทำเช่นนี้คุณกำลังมองหาที่วิธีการตั้งค่าของ*Vertex
ซึ่งไม่รวมถึงจึงใช้อินเตอร์เฟซAbs() float64
Abser