Go
interfacce
Ricerca…
Osservazioni
Le interfacce in Go sono solo set di metodi fissi. Un tipo implementa implicitamente un'interfaccia se il suo set di metodi è un superset dell'interfaccia. Non c'è una dichiarazione di intenti.
Interfaccia semplice
In Go, un'interfaccia è solo un insieme di metodi. Usiamo un'interfaccia per specificare un comportamento di un dato oggetto.
type Painter interface {
Paint()
}
Il tipo di implementazione non deve dichiarare che sta implementando l'interfaccia. È sufficiente definire i metodi della stessa firma.
type Rembrandt struct{}
func (r Rembrandt) Paint() {
// use a lot of canvas here
}
Ora possiamo usare la struttura come interfaccia.
var p Painter
p = Rembrandt{}
Un'interfaccia può essere soddisfatta (o implementata) da un numero arbitrario di tipi. Inoltre un tipo può implementare un numero arbitrario di interfacce.
type Singer interface {
Sing()
}
type Writer interface {
Write()
}
type Human struct{}
func (h *Human) Sing() {
fmt.Println("singing")
}
func (h *Human) Write() {
fmt.Println("writing")
}
type OnlySinger struct{}
func (o *OnlySinger) Sing() {
fmt.Println("singing")
}
Qui, la struttura Human soddisfa sia l'interfaccia Singer che quella Writer , ma la struttura OnlySinger soddisfa solo l'interfaccia Singer .
Interfaccia vuota
C'è un tipo di interfaccia vuoto, che non contiene metodi. Lo dichiariamo come interface{} . Questo non contiene metodi, quindi ogni type soddisfa. Quindi l'interfaccia vuota può contenere qualsiasi valore di tipo.
var a interface{}
var i int = 5
s := "Hello world"
type StructType struct {
i, j int
k string
}
// all are valid statements
a = i
a = s
a = &StructType{1, 2, "hello"}
Il caso d'uso più comune per le interfacce è garantire che una variabile supporti uno o più comportamenti. Al contrario, il caso d'uso principale per l'interfaccia vuota è definire una variabile che può contenere qualsiasi valore, indipendentemente dal suo tipo concreto.
Per riportare questi valori come i loro tipi originali, dobbiamo solo farlo
i = a.(int)
s = a.(string)
m := a.(*StructType)
o
i, ok := a.(int)
s, ok := a.(string)
m, ok := a.(*StructType)
ok indica se l' interface a è convertibile in un determinato tipo. Se non è possibile il cast ok sarà false .
Valori dell'interfaccia
Se dichiari una variabile di un'interfaccia, può memorizzare qualsiasi tipo di valore che implementa i metodi dichiarati dall'interfaccia!
Se dichiariamo h interface Singer , può memorizzare un valore di tipo Human o OnlySinger. Ciò è dovuto al fatto che tutti implementano i metodi specificati dall'interfaccia Singer .
var h Singer
h = &human{}
h.Sing()
Determinazione del tipo sottostante dall'interfaccia
In go a volte può essere utile sapere quale tipo di sottofondo ti è stato passato. Questo può essere fatto con un interruttore di tipo. Questo presuppone che abbiamo due strutture:
type Rembrandt struct{}
func (r Rembrandt) Paint() {}
type Picasso struct{}
func (r Picasso) Paint() {}
Che implementano l'interfaccia di Painter:
type Painter interface {
Paint()
}
Quindi possiamo usare questa opzione per determinare il tipo sottostante:
func WhichPainter(painter Painter) {
switch painter.(type) {
case Rembrandt:
fmt.Println("The underlying type is Rembrandt")
case Picasso:
fmt.Println("The underlying type is Picasso")
default:
fmt.Println("Unknown type")
}
}
Controllo in fase di compilazione se un tipo soddisfa un'interfaccia
Interfacce e implementazioni (tipi che implementano un'interfaccia) sono "distaccati". Quindi è una domanda legittima come verificare in fase di compilazione se un tipo implementa un'interfaccia.
Un modo per chiedere al compilatore di verificare che il tipo T implementa l'interfaccia I tentando un'assegnazione usando il valore zero per T o puntatore a T , a seconda dei casi. E potremmo scegliere di assegnare all'identificatore vuoto per evitare inutili immondizie:
type T struct{}
var _ I = T{} // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.
Se T o *T non implementa I , sarà un errore in fase di compilazione.
Questa domanda appare anche nelle FAQ ufficiali: come posso garantire che il mio tipo soddisfi un'interfaccia?
Digitare interruttore
Gli switch di tipo possono essere utilizzati anche per ottenere una variabile che corrisponda al tipo del caso:
func convint(v interface{}) (int,error) {
switch u := v.(type) {
case int:
return u, nil
case float64:
return int(u), nil
case string:
return strconv(u)
default:
return 0, errors.New("Unsupported type")
}
}
Asserzione di tipo
È possibile accedere al tipo di dati reale dell'interfaccia con Asserzione di tipo.
interfaceVariable.(DataType)
Esempio di struct MyType che implementa l'interfaccia Subber :
package main
import (
"fmt"
)
type Subber interface {
Sub(a, b int) int
}
type MyType struct {
Msg string
}
//Implement method Sub(a,b int) int
func (m *MyType) Sub(a, b int) int {
m.Msg = "SUB!!!"
return a - b;
}
func main() {
var interfaceVar Subber = &MyType{}
fmt.Println(interfaceVar.Sub(6,5))
fmt.Println(interfaceVar.(*MyType).Msg)
}
Senza .(*MyType) non saremmo in grado di accedere a Msg Field. Se proviamo interfaceVar.Msg mostrerà l'errore di compilazione:
interfaceVar.Msg undefined (type Subber has no field or method Msg)
Vai Interfacce da un Aspetto Matematico
In matematica, specialmente Set Theory , abbiamo una collezione di cose che è chiamata set e chiamiamo quelle cose come elementi . Mostriamo un set con il suo nome come A, B, C, ... o esplicitamente con il suo membro su notazione brace: {a, b, c, d, e}. Supponiamo di avere un elemento arbitrario x e un insieme Z, la domanda chiave è: "Come possiamo capire che x è membro di Z o no?". Matematica risposta a questa domanda con un concetto: Proprietà caratteristica di un insieme. La proprietà caratteristica di un insieme è un'espressione che descrive completamente il set. Ad esempio abbiamo impostato Natural Numbers che è {0, 1, 2, 3, 4, 5, ...}. Possiamo descrivere questo insieme con questa espressione: {a n | a 0 = 0, a n = a n-1 +1}. Nell'ultima espressione a 0 = 0, a n = a n-1 +1 è la proprietà caratteristica dell'insieme di numeri naturali. Se abbiamo questa espressione, possiamo costruire completamente questo set . Lascia che descriva l'insieme di numeri pari in questo modo. Sappiamo che questo set è composto da questi numeri: {0, 2, 4, 6, 8, 10, ...}. Con un'occhiata capiamo che tutti questi numeri sono anche un numero naturale , in altre parole se aggiungiamo alcune condizioni extra alla proprietà caratteristica dei numeri naturali, possiamo costruire una nuova espressione che descrive questo insieme . Quindi possiamo descrivere con questa espressione: {n | n è un membro di numeri naturali e il promemoria di n su 2 è zero}. Ora possiamo creare un filtro che ottiene la proprietà caratteristica di un set e filtra alcuni elementi desiderati per restituire elementi del nostro set. Ad esempio, se abbiamo un filtro di numeri naturali, entrambi i numeri naturali e quelli pari possono passare questo filtro, ma se abbiamo un filtro di numero pari, alcuni elementi come 3 e 137871 non possono passare il filtro.
La definizione dell'interfaccia in Go è come definire la proprietà caratteristica e il meccanismo di utilizzo dell'interfaccia come argomento di una funzione è come un filtro che rileva che l'elemento è un membro del set desiderato o meno. Descrivi questo aspetto con il codice:
type Number interface {
IsNumber() bool // the implementation filter "meysam" from 3.14, 2 and 3
}
type NaturalNumber interface {
Number
IsNaturalNumber() bool // the implementation filter 3.14 from 2 and 3
}
type EvenNumber interface {
NaturalNumber
IsEvenNumber() bool // the implementation filter 3 from 2
}
La proprietà caratteristica di Number è tutte le strutture che hanno il metodo IsNumber , per NaturalNumber tutti quelli che hanno i metodi IsNumber e IsNaturalNumber e infine EvenNumber è tutti i tipi che hanno IsNumber , IsNaturalNumber e IsEvenNumber . Grazie a questa interpretazione dell'interfaccia, possiamo facilmente capire che poiché interface{} non ha alcuna proprietà caratteristica, accetta tutti i tipi (perché non ha alcun filtro per distinguere tra valori).