# Go Generics ปี 2026: Type Parameters, Constraints และคำถามสัมภาษณ์ > เตรียมความพร้อม Go generics สำหรับการสัมภาษณ์เทคนิค ครอบคลุม type parameters, constraints, ตัวดำเนินการ tilde และการใช้งานจริงเช่น generic cache - Published: 2026-07-08 - Updated: 2026-07-08 - Author: SharpSkill - Reading time: 9 min --- Go 1.18 ได้นำ generics เข้ามาในภาษา Go ในเดือนมีนาคม 2022 และตั้งแต่นั้นมา ฟีเจอร์นี้ได้พัฒนาผ่าน Go 1.21, 1.22 และเวอร์ชันต่อๆ ไป บทความนี้ครอบคลุมคำถามสัมภาษณ์ Go generics ที่สำคัญสำหรับปี 2026 ตั้งแต่ type parameters พื้นฐานไปจนถึง pattern constraint ขั้นสูงที่ hiring manager มักถามบ่อย > **Go Generics คืออะไร?** > > Go generics ช่วยให้สามารถเขียนฟังก์ชันและ type ที่ทำงานกับ data type ใดก็ได้ ในขณะที่ยังคงรักษา type safety ในเวลา compile ต่างจาก interface ที่ใช้ type assertion ในเวลา runtime, generics จะแก้ไข type ในเวลา compile ให้ประสิทธิภาพที่ดีกว่าและตรวจจับ error ได้เร็วกว่า ## ทำความเข้าใจ Type Parameters ใน Go Type parameters เป็นรากฐานของ Go generics โดย type parameter คือ placeholder สำหรับ type ที่จะถูกระบุเมื่อใช้งานฟังก์ชันหรือ type ที่เป็น generic ```go // generic.go // Basic generic function with type parameter T func PrintSlice[T any](items []T) { for _, item := range items { fmt.Println(item) } } // Usage - type inference determines T automatically func main() { PrintSlice([]int{1, 2, 3}) // T is int PrintSlice([]string{"a", "b"}) // T is string } ``` ไวยากรณ์วงเล็บเหลี่ยม `[T any]` ประกาศ type parameter `T` ที่ถูกจำกัดด้วย `any` คอมไพเลอร์ของ Go จะอนุมาน type ที่เป็นรูปธรรมจาก argument โดยไม่จำเป็นต้องระบุ type อย่างชัดเจนในกรณีส่วนใหญ่ ## คำถามสัมภาษณ์ที่พบบ่อย: ความแตกต่างระหว่าง `any` และ `comparable` คืออะไร? ผู้สัมภาษณ์มักถามเกี่ยวกับ built-in constraints `any` และ `comparable` การเข้าใจความแตกต่างแสดงให้เห็นถึงความรู้ generics ที่มั่นคง ```go // constraints.go // any: accepts all types (alias for interface{}) func Process[T any](value T) T { return value } // comparable: only types that support == and != func Contains[T comparable](slice []T, target T) bool { for _, v := range slice { if v == target { // This comparison requires comparable return true } } return false } // This compiles Contains([]int{1, 2, 3}, 2) // This fails: slices are not comparable // Contains([][]int{{1}, {2}}, []int{1}) ``` Constraint `comparable` จำกัด type parameters เฉพาะ type ที่รองรับตัวดำเนินการเท่ากับ Slice, map และ function ถูกยกเว้นเพราะ Go ไม่ได้กำหนด equality สำหรับพวกมันในระดับภาษา ## การสร้าง Type Constraints แบบกำหนดเองด้วย Interface Go ใช้ interface เพื่อกำหนด constraints แบบกำหนดเอง ขยายสิ่งที่ฟังก์ชัน generic สามารถรับได้ [ข้อกำหนดของ Go](https://go.dev/ref/spec#Type_constraints) กำหนดวิธีที่ type element ทำงานภายใน constraint interface ```go // number.go // Custom constraint using type union type Number interface { int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 } // Generic function constrained to numeric types func Sum[T Number](values []T) T { var total T for _, v := range values { total += v // + operator works because all Number types support it } return total } func main() { fmt.Println(Sum([]int{1, 2, 3})) // 6 fmt.Println(Sum([]float64{1.5, 2.5})) // 4.0 } ``` Type union ด้วย `|` ระบุว่า type ใดบ้างที่ตรงตาม constraint วิธีนี้ให้การควบคุมมากกว่า `any` ในขณะที่หลีกเลี่ยง type assertion ในเวลา runtime ## คำถามสัมภาษณ์: อธิบายตัวดำเนินการ Tilde `~` ใน Constraints ตัวดำเนินการ tilde `~` ใน constraints จับคู่ทั้ง type และ type ทั้งหมดที่มี type นั้นเป็น underlying type คำถามนี้ทดสอบความเข้าใจเกี่ยวกับระบบ type ของ Go ```go // underlying.go // ~int matches int and any type with int as underlying type type Signed interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 } // Custom type with int as underlying type type UserID int type Temperature int64 func Abs[T Signed](value T) T { if value < 0 { return -value } return value } func main() { var id UserID = -42 var temp Temperature = -10 fmt.Println(Abs(id)) // 42 - works because ~int matches UserID fmt.Println(Abs(temp)) // 10 - works because ~int64 matches Temperature } ``` หากไม่มี `~`, constraint `int` จะจับคู่เฉพาะ type `int` เท่านั้น ไม่ใช่ type แบบกำหนดเองเช่น `UserID` Tilde ขยาย constraint เพื่อรวม type ที่สืบทอดมา ทำให้โค้ด generic มีความยืดหยุ่นมากขึ้น ## Generic Types: Struct และ Method Generic types ขยายออกไปนอกเหนือจากฟังก์ชันไปยัง struct และ method pattern นี้ปรากฏบ่อยในการใช้งานโครงสร้างข้อมูล ```go // stack.go // Generic Stack type type Stack[T any] struct { items []T } // Push adds an element to the stack func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) } // Pop removes and returns the top element func (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T // Zero value for type T return zero, false } index := len(s.items) - 1 item := s.items[index] s.items = s.items[:index] return item, true } func main() { intStack := Stack[int]{} intStack.Push(10) intStack.Push(20) val, ok := intStack.Pop() // val=20, ok=true } ``` โปรดทราบว่า method บน generic type ต้องทำซ้ำ type parameter ในวงเล็บเหลี่ยม แต่ไม่สามารถแนะนำ type parameters ใหม่ได้ Receiver `(s *Stack[T])` ผูก method กับ instantiation เฉพาะของ `Stack` ## คำถามสัมภาษณ์: ทำไม Method ไม่สามารถมี Type Parameters ของตัวเองได้? Go ห้าม type parameters เพิ่มเติมบน method ซึ่งเป็นการตัดสินใจออกแบบที่สร้างความประหลาดใจให้กับนักพัฒนาที่มาจากภาษาเช่น Java หรือ C# [ข้อเสนอ Go generics](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md) อธิบายว่าข้อจำกัดนี้มีอยู่เพื่อให้ระบบ type สามารถจัดการได้ ```go // This is NOT valid Go code type Container[T any] struct { value T } // ERROR: methods cannot have type parameters // func (c *Container[T]) Transform[U any](fn func(T) U) U { // return fn(c.value) // } // Valid alternative: use a standalone function func Transform[T, U any](c *Container[T], fn func(T) U) U { return fn(c.value) } ``` วิธีแก้ไขคือใช้ฟังก์ชันระดับบนสุดที่มีหลาย type parameters แทน method การออกแบบนี้ทำให้ method dispatch เรียบง่ายและหลีกเลี่ยงการโต้ตอบที่ซับซ้อนระหว่าง receiver type และ type parameters ของ method ## Type Inference และ Constraint Inference Type inference ของ Go ลดความยืดยาวเมื่อเรียกใช้ฟังก์ชัน generic การเข้าใจว่าเมื่อใดจำเป็นต้องใช้ type argument อย่างชัดเจนช่วยเขียนโค้ดที่สะอาดขึ้น ```go // inference.go func Map[T, R any](input []T, transform func(T) R) []R { result := make([]R, len(input)) for i, v := range input { result[i] = transform(v) } return result } func main() { numbers := []int{1, 2, 3} // Type inference: T=int, R=string inferred from arguments strings := Map(numbers, func(n int) string { return fmt.Sprintf("%d", n) }) // Explicit types sometimes required for complex cases // Map[int, string](numbers, converter) } ``` Type inference ทำงานจาก argument ของฟังก์ชันไปยัง type parameters เมื่อคอมไพเลอร์ไม่สามารถอนุมาน type จาก argument เพียงอย่างเดียว type argument ที่ชัดเจนในวงเล็บเหลี่ยมจะแก้ไขความคลุมเครือ ## Constraints Package: `cmp` และ `slices` ใน Standard Library Go 1.21 เพิ่ม package `cmp` พร้อม constraint `Ordered` และฟังก์ชันเปรียบเทียบ Package `slices` แสดงโค้ด generic แบบ idiomatic ใน standard library ```go // stdlib.go import ( "cmp" "slices" ) func main() { numbers := []int{3, 1, 4, 1, 5, 9} // slices.Sort uses cmp.Ordered constraint internally slices.Sort(numbers) // [1, 1, 3, 4, 5, 9] // Binary search on sorted slice index, found := slices.BinarySearch(numbers, 4) // cmp.Compare returns -1, 0, or 1 result := cmp.Compare(3, 5) // -1 // cmp.Or returns first non-zero value value := cmp.Or(0, 0, 42, 100) // 42 } ``` [เอกสาร standard library](https://pkg.go.dev/cmp) แสดงให้เห็นว่า package เหล่านี้ใช้ประโยชน์จาก generics สำหรับการดำเนินการที่ type-safe บน type ที่เรียงลำดับได้ ความคุ้นเคยกับ package เหล่านี้แสดงให้เห็นถึงความรู้ generics เชิงปฏิบัตินอกเหนือจากความเข้าใจเชิงทฤษฎี ## คำถามสัมภาษณ์: การใช้งาน Generic Cache แบบฝึกหัดสัมภาษณ์ที่พบบ่อยขอให้ผู้สมัครใช้งาน cache แบบ generic ที่ thread-safe สิ่งนี้ทดสอบความรู้ generics, concurrency ด้วย [package sync](/technologies/go/interview-questions/sync-primitives) และการออกแบบ API ```go // cache.go import ( "sync" "time" ) type Cache[K comparable, V any] struct { mu sync.RWMutex items map[K]cacheItem[V] } type cacheItem[V any] struct { value V expiration time.Time } func NewCache[K comparable, V any]() *Cache[K, V] { return &Cache[K, V]{ items: make(map[K]cacheItem[V]), } } func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) { c.mu.Lock() defer c.mu.Unlock() c.items[key] = cacheItem[V]{ value: value, expiration: time.Now().Add(ttl), } } func (c *Cache[K, V]) Get(key K) (V, bool) { c.mu.RLock() defer c.mu.RUnlock() item, exists := c.items[key] if !exists || time.Now().After(item.expiration) { var zero V return zero, false } return item.value, true } ``` การใช้งานนี้ใช้ `comparable` สำหรับ key (ข้อกำหนดของ map) และ `any` สำหรับ value Struct `cacheItem` แยกต่างหากแสดงให้เห็นว่า generic type สามารถซ้อนกันได้อย่างไร Thread safety มาจาก `sync.RWMutex` ซึ่งเป็น pattern ที่กล่าวถึงใน [การสัมภาษณ์ concurrency](/technologies/go/interview-questions/concurrency-patterns) ของ Go ## Zero Values และ Type Constraints การจัดการ zero values ในโค้ด generic ต้องการความเข้าใจว่า Go เริ่มต้นตัวแปรของ parameterized type อย่างไร ```go // zero.go // Return zero value when slice is empty func First[T any](slice []T) T { if len(slice) == 0 { var zero T // Zero value: 0 for int, "" for string, nil for pointers return zero } return slice[0] } // Alternative: return pointer to avoid ambiguity func FirstOrNil[T any](slice []T) *T { if len(slice) == 0 { return nil } return &slice[0] } ``` Pattern `var zero T` สร้าง zero value สำหรับ type `T` ใดก็ได้ สำหรับ type ที่ zero เป็นค่าที่ถูกต้อง (เช่น `0` สำหรับ integer) รูปแบบที่คืน pointer จะแยกแยะระหว่าง "ไม่พบ" และ "พบ zero" ## ขั้นสูง: การรวม Constraints หลายตัว ฟังก์ชัน generic ที่ซับซ้อนอาจต้องการให้ type ตรงตาม constraints หลายตัว Go จัดการสิ่งนี้ผ่าน interface embedding ```go // combined.go // Constraint requiring both ordering and string conversion type Stringable interface { String() string } type OrderedStringable interface { cmp.Ordered Stringable } // Alternative: use type parameters with multiple constraints inline func PrintSorted[T interface{ cmp.Ordered; fmt.Stringer }](items []T) { slices.Sort(items) for _, item := range items { fmt.Println(item.String()) } } ``` Interface embedding รวม constraints เข้าด้วยกัน ต้องการให้ type ใช้งานทุก interface ที่ถูก embed ไวยากรณ์ inline `interface{ A; B }` ให้ฟังก์ชันเดียวกันโดยไม่ต้องประกาศ constraint type ที่มีชื่อ ## สรุป - Type parameters ด้วย `[T any]` ช่วยให้เขียนโค้ดที่ใช้ซ้ำได้และ type-safe โดยไม่ต้องใช้ runtime reflection - `comparable` จำกัดเฉพาะ type ที่รองรับ `==` และ `!=` ซึ่งจำเป็นสำหรับ map keys - ตัวดำเนินการ tilde `~` จับคู่ type ที่มี underlying type เฉพาะ ขยายความยืดหยุ่นของ constraint - Constraints แบบกำหนดเองใช้ type union (`int | string`) เพื่อระบุ type ที่อนุญาตอย่างชัดเจน - Method ไม่สามารถมี type parameters ของตัวเองได้ ใช้ฟังก์ชันระดับบนสุดเป็นทางเลือก - Package standard library `cmp` และ `slices` แสดง pattern generic แบบ idiomatic - Zero values ผ่าน `var zero T` จัดการข้อมูลว่างหรือหายไปในฟังก์ชัน generic - โครงสร้างข้อมูล generic ที่ thread-safe รวม generics กับ sync primitives --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/go/go-generics-type-parameters-constraints-interview-questions