# Interface Go ขั้นสูงในปี 2026: การประกอบ Type Assertion และคำถามสัมภาษณ์ > คู่มือเชิงลึกเกี่ยวกับ interface Go สมัยใหม่: การประกอบ interface, type assertion ที่ปลอดภัย, generic แบบอ้างอิงตนเอง และคำถามสัมภาษณ์ที่พบบ่อยสำหรับนักพัฒนา Go - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: go, interfaces, generics, type-assertion, interview - Reading time: 12 min --- Interface ใน Go กำหนดพฤติกรรมโดยไม่ระบุการ implement ทำให้เป็นหัวใจสำคัญของการเขียนโค้ดที่ยืดหยุ่นและทดสอบได้ Go 1.26 ขยายความสามารถนี้ด้วย generic แบบอ้างอิงตนเอง, iterator reflection ใหม่ และการจัดการ error แบบ type-safe ผ่าน `errors.AsType` คู่มือนี้ครอบคลุมการประกอบ interface, type assertion, รูปแบบ embedding และคำถามที่มักเกิดขึ้นในการสัมภาษณ์ทางเทคนิค > **การปรับปรุง Interface ใน Go 1.26** > > ประเภท generic สามารถอ้างอิงตนเองในรายการ type parameter ได้แล้ว: `type Adder[A Adder[A]] interface { Add(A) A }` รูปแบบ F-bounded polymorphism นี้ทำให้ interface สามารถจำกัด return type ให้เป็นประเภทที่ implement ได้ ## การประกอบ Interface และรูปแบบ Embedding การ embed interface รวมหลาย interface เข้าด้วยกันเป็น contract เดียว ผลลัพธ์คือ interface ใหม่ที่ต้องการ method ทั้งหมดจาก interface ที่ถูก embed รูปแบบนี้หลีกเลี่ยงการซ้ำซ้อนและสร้างขอบเขตนามธรรมที่ชัดเจน ```go // interfaces.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error } // Interface ที่ประกอบจากการ embed สาม interface type ReadWriteCloser interface { Reader Writer Closer } ``` ประเภทใดก็ตามที่ implement method `Read`, `Write` และ `Close` จะตรงตามข้อกำหนดของ `ReadWriteCloser` โดยอัตโนมัติ [Standard library ของ Go](https://pkg.go.dev/io) ใช้รูปแบบนี้อย่างกว้างขวางใน package `io` คำถามสัมภาษณ์ที่พบบ่อยขอให้ผู้สมัครอธิบายว่าทำไม Go จึงชอบ interface ที่เล็กและมุ่งเน้น คำตอบอยู่ที่ความสามารถในการประกอบ: `io.Reader` ปรากฏในฟังก์ชันหลายร้อยตัวเพราะต้องการเพียง method เดียว Interface ที่ใหญ่กว่าสร้าง coupling ที่แน่นขึ้นและลดการนำกลับมาใช้ซ้ำ ## Type Assertion: ไวยากรณ์และความปลอดภัย Type assertion ดึงประเภทที่เป็นรูปธรรมจากค่า interface รูปแบบสองค่าป้องกัน panic โดยคืน boolean ที่บอกความสำเร็จ ```go // assertions.go func processValue(v interface{}) { // Assertion สองค่า: ปลอดภัย ไม่ panic if str, ok := v.(string); ok { fmt.Printf("ค่า string: %s\n", str) return } // Type switch สำหรับหลายประเภท switch val := v.(type) { case int: fmt.Printf("Integer: %d\n", val) case float64: fmt.Printf("Float: %.2f\n", val) case []byte: fmt.Printf("Bytes: %x\n", val) default: fmt.Printf("ประเภทไม่รู้จัก: %T\n", val) } } ``` Type switch จัดการหลายประเภทได้อย่างสะอาด แต่ละ case ผูก `val` กับประเภทที่ assert ภายใน block ของมัน ทำให้ไม่จำเป็นต้อง assert แยก > **ความเสี่ยง Panic** > > Assertion ค่าเดียวเช่น `str := v.(string)` จะ panic ถ้า assertion ล้มเหลว โค้ด production ควรใช้รูปแบบสองค่าหรือ type switch เสมอ ผู้สัมภาษณ์มักถามเกี่ยวกับประสิทธิภาพของ type assertion Runtime ทำการเปรียบเทียบ type descriptor เพียงครั้งเดียว ทำให้ assertion ไม่แพง ค่าใช้จ่ายเพิ่มขึ้นกับค่า interface ที่ห่อ pointer ไปยัง struct ขนาดใหญ่เนื่องจาก indirection แต่ assertion เองยังคงเป็น O(1) ## Generic แบบอ้างอิงตนเองใน Go 1.26 Go 1.26 แนะนำ type parameter แบบอ้างอิงตนเอง ทำให้ interface ที่ method ต้องคืนประเภทที่ implement รูปแบบนี้บางครั้งเรียกว่า [F-bounded polymorphism](https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification) แก้ข้อจำกัดที่มีมานาน ```go // builder.go // Interface อ้างอิงตนเอง: method คืนประเภทเดียวกัน type Builder[B Builder[B]] interface { WithName(name string) B WithAge(age int) B Build() string } type PersonBuilder struct { name string age int } func (p PersonBuilder) WithName(name string) PersonBuilder { p.name = name return p } func (p PersonBuilder) WithAge(age int) PersonBuilder { p.age = age return p } func (p PersonBuilder) Build() string { return fmt.Sprintf("%s, %d ปี", p.name, p.age) } // ฟังก์ชัน generic ใช้ constraint อ้างอิงตนเอง func configure[B Builder[B]](b B, name string, age int) string { return b.WithName(name).WithAge(age).Build() } ``` Constraint `Builder[B]` รับประกันว่า `WithName` และ `WithAge` คืน `B` ไม่ใช่ generic `Builder` หากไม่มีสิ่งนี้ return type จะเป็น interface ทำให้สูญเสียข้อมูลประเภทที่เป็นรูปธรรมและทำลาย method chaining ประเภททางคณิตศาสตร์ได้ประโยชน์จากรูปแบบนี้ Interface `Addable[A Addable[A]]` รับประกันว่า `Add(A) A` คืนประเภทตัวเลขเดียวกัน ป้องกันการผสมค่า `BigInt` และ `Decimal` โดยไม่ตั้งใจ ## errors.AsType: Error Unwrapping แบบ Type-Safe Go 1.26 เพิ่ม [`errors.AsType`](https://pkg.go.dev/errors#AsType) แทนที่รูปแบบ pointer-dance ของ `errors.As` เวอร์ชัน generic คืน error ที่ unwrap โดยตรง ```go // errors_handling.go type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("การตรวจสอบล้มเหลวที่ %s: %s", e.Field, e.Message) } func handleError(err error) { // Go 1.26: generic type-safe unwrapping if valErr, ok := errors.AsType[*ValidationError](err); ok { log.Printf("ข้อผิดพลาดการตรวจสอบที่ field %s\n", valErr.Field) return } // ก่อน Go 1.26: pointer-based unwrapping // var valErr *ValidationError // if errors.As(err, &valErr) { ... } log.Printf("ข้อผิดพลาดที่ไม่คาดคิด: %v\n", err) } ``` API ใหม่นี้ขจัดการประกาศตัวแปรแยกต่างหากและทำให้ประเภทเป้าหมายชัดเจนในการเรียกฟังก์ชัน Error chain ถูกค้นหาแบบเดียวกับก่อน; เฉพาะ interface ที่เปลี่ยน ## Iterator Reflection สำหรับการตรวจสอบ Interface Go 1.26 เพิ่ม method iterator ให้กับ package `reflect` `Type.Methods()` และ `Value.Methods()` คืน iterator สำหรับการวนซ้ำ method แทนที่ loop แบบ index ```go // reflection.go import "reflect" func inspectInterface(v interface{}) { t := reflect.TypeOf(v) // Go 1.26: การตรวจสอบ method แบบ iterator fmt.Printf("Method ของประเภท %s:\n", t.Name()) for method := range t.Methods() { fmt.Printf(" %s: %s\n", method.Name, method.Type) } // สำหรับ field ของ struct (ใหม่ใน 1.26 เช่นกัน) if t.Kind() == reflect.Struct { for field := range t.Fields() { fmt.Printf(" Field: %s (%s)\n", field.Name, field.Type) } } } ``` รูปแบบ iterator สอดคล้องกับฟีเจอร์ range-over-function ของ Go 1.23 โค้ดอ่านง่ายขึ้น และไม่มี penalty ด้านประสิทธิภาพ: iterator yield ค่าแบบ lazy ## การตรวจสอบ Interface Satisfaction ตอน Compile Time Go ตรวจสอบ interface satisfaction ตอน compile time เมื่อกำหนดค่าที่เป็นรูปธรรมให้กับตัวแปร interface การตรวจสอบชัดเจนโดยใช้ blank identifier assignment จับข้อผิดพลาดได้เร็วใน codebase ขนาดใหญ่ ```go // compile_check.go type Storage interface { Save(key string, data []byte) error Load(key string) ([]byte, error) Delete(key string) error } type FileStorage struct { basePath string } // การตรวจสอบ compile-time: ล้มเหลวถ้า FileStorage ขาด method var _ Storage = (*FileStorage)(nil) func (f *FileStorage) Save(key string, data []byte) error { path := filepath.Join(f.basePath, key) return os.WriteFile(path, data, 0644) } func (f *FileStorage) Load(key string) ([]byte, error) { path := filepath.Join(f.basePath, key) return os.ReadFile(path) } func (f *FileStorage) Delete(key string) error { return os.Remove(filepath.Join(f.basePath, key)) } ``` บรรทัด `var _ Storage = (*FileStorage)(nil)` compile ได้เฉพาะเมื่อ `*FileStorage` ตรงตามข้อกำหนด `Storage` รูปแบบนี้จับ method ที่ขาดทันทีแทนที่จะตอน runtime เมื่อค่าถูกกำหนด ## Empty Interface และ Type Constraint Empty interface `interface{}` ยอมรับค่าใดก็ได้ ขณะที่ `any` เป็น alias ของมันตั้งแต่ Go 1.18 Generic constraint ให้ type safety ตอน compile time โดยไม่ต้อง runtime assertion ```go // constraints.go import "golang.org/x/exp/constraints" // ฟังก์ชัน generic กับ constraint ตัวเลข func Sum[T constraints.Integer | constraints.Float](values []T) T { var total T for _, v := range values { total += v } return total } // Constraint comparable สำหรับ map key func Contains[K comparable, V any](m map[K]V, key K) bool { _, exists := m[key] return exists } ``` การเลือก generic แทน `interface{}` ขจัด type assertion และจับ type mismatch ตอน compile time Trade-off คือความซับซ้อนเพิ่มขึ้นใน signature ฟังก์ชัน ดังนั้น generic เหมาะที่สุดเมื่อฟังก์ชันทำงานกับหลายประเภทจริงๆ ## คำถามสัมภาษณ์ทางเทคนิคเกี่ยวกับ Interface Go ผู้สัมภาษณ์ทดสอบความรู้ interface หลายระดับ ต่อไปนี้คือคำถามทั่วไปพร้อมคำตอบสั้นๆ **ถ: เกิดอะไรขึ้นเมื่อเรียก method บนค่า interface ที่เป็น nil เทียบกับค่าที่เป็นรูปธรรมที่เป็น nil ภายใน interface?** Interface ที่เป็น nil ไม่มีประเภทและไม่มีค่า; การเรียก method ใดก็ตามจะ panic Interface ที่ไม่ใช่ nil ที่มี pointer ที่เป็น nil มีประเภท; method ทำงานกับ receiver ที่เป็น nil พฤติกรรมนี้ทำให้รูปแบบเช่น `(*bytes.Buffer)(nil).String()` คืน string ว่างได้ **ถ: การเปรียบเทียบ interface ทำงานอย่างไร?** ค่า interface สองค่าเท่ากันถ้ามี dynamic type เดียวกันและ dynamic value เท่ากัน การเปรียบเทียบ interface กับประเภทที่เปรียบเทียบไม่ได้ (slice, map, function) จะ panic ตอน runtime **ถ: เมื่อใดควรใช้ pointer receiver เทียบกับ value receiver?** Pointer receiver อนุญาตให้ mutation และหลีกเลี่ยงการ copy struct ขนาดใหญ่ Value receiver ปลอดภัยสำหรับการใช้งาน concurrent และทำงานกับทั้งค่าและ pointer ถ้า method ใดต้องการ pointer receiver ทุก method บนประเภทนั้นควรใช้ pointer receiver เพื่อความสม่ำเสมอ เนื่องจากเฉพาะ `*T` เท่านั้นที่ตรงตามข้อกำหนด interface ที่ต้องการ method แบบ pointer-receiver ```go // receiver_rules.go type Counter struct { count int } // Pointer receiver: แก้ไข struct func (c *Counter) Increment() { c.count++ } // Value receiver: การดำเนินการ read-only func (c Counter) Value() int { return c.count } type Incrementer interface { Increment() } func main() { var c Counter // var i Incrementer = c // Error compile: Counter ไม่มี Increment var i Incrementer = &c // OK: *Counter มี Increment i.Increment() } ``` **ถ: อธิบายหลักการ "accept interfaces, return structs"** ฟังก์ชันที่ยอมรับ interface แยกตัวจาก implementation ที่เป็นรูปธรรม ทำให้ทดสอบด้วย mock ได้ การคืนประเภทที่เป็นรูปธรรมให้ผู้เรียกเข้าถึง method ของประเภทได้เต็มที่โดยไม่ต้อง type assertion การคืน interface ซ่อนการเพิ่ม method ในอนาคตไว้หลัง type assertion สำหรับการเตรียมสัมภาษณ์ Go เพิ่มเติม ดูโมดูล [คำถาม concurrency Go](/technologies/go/interview-questions/concurrency-patterns) และ [โมดูล testing](/technologies/go/interview-questions/testing) > **เคล็ดลับสัมภาษณ์** > > เมื่อถูกขอให้ออกแบบ interface เริ่มด้วยกรณี single-method ขยายเมื่อหลาย method ถูกเรียกพร้อมกันเสมอ Interface `Stringer`, `Reader` และ `Handler` ของ standard library แต่ละตัวกำหนด method เดียว ## สิ่งที่นักพัฒนา Go อาวุโสควรรู้เกี่ยวกับ Interface - การประกอบ interface ผ่าน embedding สร้าง contract ที่ยืดหยุ่นโดยไม่มีลำดับชั้น inheritance รักษา interface ให้เล็ก: หนึ่งถึงสาม method ครอบคลุม use case ส่วนใหญ่ - Type assertion และ type switch ดึงประเภทที่เป็นรูปธรรมอย่างปลอดภัย ใช้รูปแบบสองค่าหรือ switch ในโค้ด production เสมอเพื่อหลีกเลี่ยง panic - Generic แบบอ้างอิงตนเองของ Go 1.26 ทำให้ builder pattern และประเภททางคณิตศาสตร์ที่ method ต้องคืนประเภทที่ implement เป็นไปได้ - `errors.AsType` ทำให้ error unwrapping ง่ายขึ้นด้วย API แบบ generic, type-safe ใช้ในโค้ดใหม่ที่กำหนดเป้าหมาย Go 1.26 หรือใหม่กว่า - การตรวจสอบ interface ตอน compile-time ด้วย `var _ Interface = (*Type)(nil)` จับ method ที่ขาดก่อนการรัน test - เลือก generic แทน `interface{}` เมื่อ type safety ตอน compile time สำคัญกว่าความซับซ้อนของ signature - แนวทาง "accept interfaces, return structs" รักษา API ให้ยืดหยุ่นสำหรับผู้เรียกขณะเปิดเผยฟังก์ชันการทำงานเต็มที่ --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/go/go-interfaces-advanced-composition-type-assertions-2026