در سادهترین شکل، یک نوع واسط مجموعهای از «امضاهای متد» است.
در ادامه نمونهای از تعریف یک واسط را میبینید که شامل دو متد Add و Value است:
type Counter interface {
Add(increment int)
Value() int
}
نام پارامترها مانند increment را میتوان از تعریف واسط حذف کرد، اما وجودشان معمولاً خوانایی را بیشتر میکند.
اسم واسطها در Go شامل کلمهی Interface یا I نیست.
در عوض، اغلب به er ختم میشوند، مثلاً Reader و Stringer.
هر نوعی که متدهای یک واسط را تعریف کند، بهطور خودکار و ضمنی آن واسط را پیادهسازی میکند.
در Go کلیدواژهی implements وجود ندارد.
نوع زیر واسط Counter را که در بالا دیدیم پیادهسازی میکند.
type Stats struct {
value int
// ...
}
func (s Stats) Add(v int) {
s.value += v
}
func (s Stats) Value() int {
return s.value
}
func (s Stats) SomeOtherMethod() {
// The type can have additional methods not mentioned in the interface.
}
برای پیادهسازی واسط، مهم نیست که متد گیرندهی مقداری داشته باشد یا گیرندهی اشارهگر. (اگر در این مورد مطمئن نیستید، مفاهیم متدها را دوباره ببینید.)
یک مقدار از نوع واسط میتواند هر مقداری را در خود نگه دارد، به شرط آنکه آن متدها را پیادهسازی کند. 1
یعنی اکنون میتوان از Stats در همهی جاهایی استفاده کرد که انتظار واسط Counter را دارند.
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
اگر همهی پارامترها و مقادیر بازگشتی توابع مجبور بودند از نوع مشخص (یعنی غیرواسط) باشند، این موضوع بهشدت قابلیت استفادهی مجدد توابع را محدود میکرد. با استفاده از نوعهای واسط برای پارامترها، فقط رفتاری تعریف میشود که تابع برای انجام کارش به آن نیاز دارد. اکنون میتوان آن تابع را با پارامترهایی از نوعهای مشخص متفاوت به کار برد، به شرط آنکه آن نوعهای مشخص واسط را پیادهسازی کنند. همین منطق در مورد استفاده از نوعهای واسط به عنوان مقدار بازگشتی نیز برقرار است.
چون واسطها بهصورت ضمنی پیادهسازی میشوند، یک نوع بهراحتی میتواند چندین واسط را پیادهسازی کند. فقط کافی است همهی متدهای لازم را تعریف کرده باشد.
تعریفهای واسط میتوانند واسطهای دیگر را در خود جای دهند. نمونهای از کتابخانهی استاندارد را ببینید:
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
تعریف واسط ReadCloser به جای اینکه امضاهای متد Read و Close را تکرار کند، از تعریفهای موجود Reader و Closer استفاده میکند.
در Go یک نوع واسط بسیار خاص وجود دارد: واسط خالی که صفر متد دارد.
واسط خالی را اینطور مینویسند: interface{}.
در Go 1.18 یا بالاتر میتوان از any هم استفاده کرد.
این کلمه به عنوان نام جایگزینی برای آن تعریف شده است.
چون واسط خالی هیچ متدی ندارد، هر نوعی آن را بهصورت ضمنی پیادهسازی میکند. این ویژگی هنگام تعریف تابعی مفید است که بتواند بهطور کلی هر مقداری را بپذیرد. در چنین حالتی، پارامتر تابع از نوع واسط خالی استفاده میکند.
برای مثال:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}