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).



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow