receiver, interface เล็ก, assertion, embedding
type Person struct{ Name string }
// (p Person) = บอกว่านี่เป็นของ Person
func (p Person) Greet() string {
return "สวัสดี " + p.Name
}
p := Person{Name: "Ann"}
fmt.Println(p.Greet()) // เรียกผ่านตัวเลย ไม่ต้อง Greet(p)แปะได้กับ type ที่เราสร้างเองใน package เดียวกัน (ยกเว้น pointer interface โดยตรง)
func (p Person) Read() string { return p.Name } // อ่านอย่างเดียว -> value ได้
func (p *Person) Grow() { p.Age++ } // ต้องแก้ของจริง -> ต้อง *กฎจำง่าย:
* ทั้งหมด* ให้เป็น * ทั้ง type ไม่งั้น map/slice งง// ดี: เป็น * ทั้งคู่
func (b *Buffer) Write(p []byte) (int, error)
func (b *Buffer) String() stringtype Stringer interface{ String() string } // มี 1 เมธอด = เป็นพวกเดียวกันอัตโนมัติ
func Print(s fmt.Stringer) { fmt.Println(s.String()) }implementsio.Reader io.Writer มีเมธอดเดียวแต่ใช้ทั้งโลก)ชื่อ interface เมธอดเดียวให้ลงท้าย er: Reader Writer Formatter Stringer
// แบบปลอดภัยมี ,ok
if s, ok := v.(fmt.Stringer); ok {
fmt.Println(s.String())
}
// เช็คหลาย type
switch t := t.(type) {
default:
fmt.Printf("ไม่รู้จัก %T\n", t)
case bool:
fmt.Println("bool", t)
case int:
fmt.Println("int", t)
case *int:
fmt.Println("pointer int", *t)
}ทริก t := t.(type) ในแต่ละ case t มี type ต่างกัน ใช้ชื่อเดิมได้เลย
ยัดกล่องในกล่อง เรียกของข้างในได้เลย (แทน inheritance):
type Logger struct{}
func (Logger) Log(s string) { fmt.Println("log:", s) }
type Job struct {
Command string
Logger // ไม่ต้องตั้งชื่อ field
}
j := Job{Command: "run"}
j.Log("start") // เหมือนเป็นของตัวเองฝัง interface ก็ได้:
ของใหม่ 1.27: ตั้งค่า field ชั้นในได้เลยไม่ต้องห่อ:
type Habitat struct{ Burrow string }
type Gopher struct {
Name string
Habitat
}
g := Gopher{Name: "Gopher", Burrow: "Burrow #42"}go fix embedlit ช่วยเปลี่ยนแบบเก่าเป็นแบบใหม่ให้
Read Write String ถ้าความหมาย+signature ไม่เหมือน std (คนอ่านสับสน)String() ห้ามเรียก Sprintf("%s", ตัวเอง) ตรงๆ จะวนไม่จบ ให้แปลงก่อน string(m)any ถ้ารู้ type ชัด (เสีย type safety)!= nil นะ เช็กดีๆอ้างอิง: Effective Go #methods #interfaces #embedding, Go 1.27 struct literal keys
func WriteAll(w io.Writer, lines []string) error {
for _, l := range lines {
if _, err := io.WriteString(w, l+"\n"); err != nil {
return err
}
}
return nil // รับ interface -> ใช้ได้ทั้งไฟล์ socket buffer
}type Reader interface{ Read([]byte) (int, error) }
type Writer interface{ Write([]byte) (int, error) }
type ReadWriter interface {
Reader
Writer // ฝัง 2 เล็กเป็น 1 ใหญ่
}var p *User // nil
var u fmt.Stringer = p
fmt.Println(u == nil) // false! interface ห่อ (type, nil) ไว้ ไม่ใช่ nil เปล่าio.Reader) คืน struct ชัดๆ (เรียกง่าย เทสต์ง่าย)Read/Write/String) ถ้า signature ไม่เหมือนString() ห้าม Sprintf("%s", ตัวเอง) ตรงๆ จะวนไม่จบ!= nil หลอกตา)v, ok := x.(T) อย่า assert ตรงๆ ถ้าไม่ชัวร์any ถ้ารู้ type ชัด (เสีย type safety เปล่าๆ)Store interface ให้ todo app แล้วทำ 2 impl (memory + file)