A legegyszerűbb formájában egy interfész típus metódusszignatúrák egy halmaza.
Íme egy példa egy interfészdefinícióra, amely két metódust, az Add-ot és a Value-t tartalmazza:
type Counter interface {
Add(increment int)
Value() int
}
A paraméternevek, mint például az increment, elhagyhatók az interfészdefinícióból, de gyakran növelik az olvashatóságot.
A Go-ban az interfésznevek nem tartalmazzák az Interface vagy az I szót.
Ehelyett gyakran er-re végződnek, például Reader, Stringer.
Minden típus, amely definiálja az interfész metódusait, automatikusan, implicit módon „implementálja” az interfészt.
A Go-ban nincs implements kulcsszó.
A következő típus implementálja a fentebb látott Counter interfészt.
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.
}
Az interfész implementálásához nem számít, hogy a metódusnak érték- vagy pointer-receiver-e van. (Nézd át újra a metódusokról szóló fogalmakat, ha nem vagy biztos bennük.)
Egy interfész típusú érték bármilyen olyan értéket tárolhat, amely implementálja ezeket a metódusokat. 1
Ez azt jelenti, hogy a Stats mostantól minden olyan helyen használható, ahol a Counter interfészt várják.
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
Ha minden függvényparaméternek és visszatérési értéknek konkrét (azaz nem interfész) típusúnak kellene lennie, az erősen korlátozná az újrafelhasználhatóságukat. Azzal, hogy a paraméterekhez interfész típusokat használunk, csak azt a viselkedést definiáljuk, amelyre a függvénynek a feladata elvégzéséhez szüksége van. Így a függvény különböző konkrét típusú paraméterekkel is használható, amíg azok a konkrét típusok implementálják az interfészt. Ugyanez a logika érvényes akkor is, amikor interfész típusokat visszatérési értékként használunk.
Mivel az interfészek implicit módon implementálódnak, egy típus könnyedén implementálhat több interfészt is. Ehhez csak az összes szükséges metódust kell definiálnia.
Az interfészdefiníciók tartalmazhatnak más interfészeket. Íme egy példa a szabványos könyvtárból.
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
Ahelyett, hogy megismételné a Read és a Close metódusszignatúráit a ReadCloser definiálásához, az interfészdefiníció a Reader és a Closer meglévő definícióit használja fel.
A Go-ban van egy nagyon különleges interfész típus, az üres interfész, amely nulla metódust tartalmaz.
Az üres interfészt így írjuk le: interface{}.
A Go 1.18-as vagy újabb verzióiban az any is használható.
Alias-ként lett definiálva.
Mivel az üres interfésznek nincsenek metódusai, minden típus implicit módon implementálja. Ez hasznos, ha olyan függvényt akarunk definiálni, amely általánosan bármilyen értéket elfogadhat. Ebben az esetben a függvényparaméter az üres interfész típust használja.
Például:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}