Sök…


Anmärkningar

Gränssnitt i Go är bara fasta metoduppsättningar. En typ implementerar implicit ett gränssnitt om dess metoduppsättning är en superset av gränssnittet. Det finns ingen avsiktsförklaring.

Enkelt gränssnitt

I Go är ett gränssnitt bara en uppsättning metoder. Vi använder ett gränssnitt för att specificera ett beteende hos ett visst objekt.

type Painter interface {
    Paint()
}

Implementeringstypen behöver inte förklara att den implementerar gränssnittet. Det räcker med att definiera metoder för samma signatur.

type Rembrandt struct{}

func (r Rembrandt) Paint() {
    // use a lot of canvas here 
}

Nu kan vi använda strukturen som gränssnitt.

var p Painter
p = Rembrandt{}

Ett gränssnitt kan tillfredsställas (eller implementeras) av ett godtyckligt antal typer. En typ kan också implementera ett godtyckligt antal gränssnitt.

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")
}

Här uppfyller The Human struct både Singer och Writer gränssnittet, men OnlySinger strukturen uppfyller endast Singer gränssnittet.


Tomt gränssnitt

Det finns en tom gränssnitttyp som inte innehåller några metoder. Vi förklarar det som interface{} . Detta innehåller inga metoder så alla type uppfyller det. Därför kan det tomma gränssnittet innehålla valfritt typvärde.

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"}

Det vanligaste fallet för gränssnitt är att se till att en variabel stöder ett eller flera beteenden. Däremot är det primära användningsfallet för det tomma gränssnittet att definiera en variabel som kan innehålla valfritt värde, oavsett dess konkreta typ.

För att få tillbaka dessa värden som sina ursprungliga typer behöver vi bara göra

i = a.(int)
s = a.(string)
m := a.(*StructType)

eller

i, ok := a.(int)
s, ok := a.(string)
m, ok := a.(*StructType)

ok anger om interface a är konvertibelt till en viss typ. Om det inte är möjligt att kasta ok kommer det att vara false .


Gränssnittsvärden

Om du deklarerar en variabel i ett gränssnitt kan det lagra valfri värdetyp som implementerar de metoder som deklareras av gränssnittet!

Om vi förklarar h för interface Singer , kan det lagra ett värde av typen Human eller OnlySinger. Detta beror på att alla implementerar metoder som anges av Singer gränssnittet.

var h Singer
h = &human{}

h.Sing()

Bestämma underliggande typ från gränssnittet

I farten kan det ibland vara användbart att veta vilken underliggande typ du har passerat. Detta kan göras med en typomkopplare. Detta förutsätter att vi har två strukturer:

type Rembrandt struct{}

func (r Rembrandt) Paint() {}

type Picasso struct{}

func (r Picasso) Paint() {}

Som implementerar Painter-gränssnittet:

type Painter interface {
    Paint()
}

Sedan kan vi använda denna switch för att bestämma den underliggande typen:

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")
    }
}

Samla tidskontroll om en typ uppfyller ett gränssnitt

Gränssnitt och implementationer (typer som implementerar ett gränssnitt) "lossas". Så det är en rättslig fråga hur man kontrollerar vid sammanställningstid om en typ implementerar ett gränssnitt.

Ett sätt att be kompilatorn kontrollera att typen T implementerar gränssnittet I är genom att försöka en tilldelning med nollvärdet för T eller pekaren till T , efter behov. Och vi kan välja att tilldela den tomma identifieraren för att undvika onödigt skräp:

type T struct{}

var _ I = T{}       // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.

Om T eller *T inte implementerar I kommer det att vara ett kompileringstidsfel.

Denna fråga visas också i den officiella FAQ: Hur kan jag garantera att min typ uppfyller ett gränssnitt?

Skriv omkopplare

Typomkopplare kan också användas för att få en variabel som matchar fallet:

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")
    }
}

Skriv påstående

Du kan få åtkomst till den verkliga datatypen för gränssnittet med typpåståendet.

interfaceVariable.(DataType)

Exempel på MyType som implementerar gränssnitt 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)
}

Utan .(*MyType) skulle vi inte komma åt Msg fältet. Om vi försöker interfaceVar.Msg kommer det att visa kompileringsfel:

interfaceVar.Msg undefined (type Subber has no field or method Msg)

Gå gränssnitt från en matematisk aspekt

I matematik, särskilt Set Theory , har vi en samling saker som kallas set och vi benämner dessa saker som element . Vi visar en uppsättning med dess namn som A, B, C, ... eller uttryckligen med att sätta sin medlem på brace-notation: {a, b, c, d, e}. Anta att vi har ett godtyckligt element x och en uppsättning Z, nyckelfrågan är: "Hur kan vi förstå att x är medlem i Z eller inte?". Matematiker svarar på denna fråga med ett koncept: Karakteristisk egenskap hos en uppsättning. Karakteristisk egenskap hos en uppsättning är ett uttryck som beskriver uppsättningen fullständigt. Vi har till exempel uppsättning av naturliga nummer som är {0, 1, 2, 3, 4, 5, ...}. Vi kan beskriva denna uppsättning med detta uttryck: {a n | a 0 = 0, a n = a n-1 +1}. I sista uttrycket är 0 = 0, a n = a n-1 +1 den karakteristiska egenskapen för uppsättning av naturliga nummer. Om vi har detta uttryck kan vi bygga denna uppsättning helt . Låt oss beskriva uppsättningen med jämna siffror på detta sätt. Vi vet att denna uppsättning görs av dessa siffror: {0, 2, 4, 6, 8, 10, ...}. Med en överblick förstår vi att allt detta nummer också är ett naturligt tal , med andra ord om vi lägger till några extra villkor till karakteristiska egenskaper hos naturliga nummer, kan vi bygga ett nytt uttryck som beskriver denna uppsättning . Så vi kan beskriva med detta uttryck: {n | n är en medlem av naturliga siffror och påminnelsen om n på 2 är noll}. Nu kan vi skapa ett filter som får en sängs karakteristiska egenskap och filtrera några önskade element för att returnera element i vår uppsättning. Om vi till exempel har ett naturligt talfilter kan både naturliga nummer och jämna nummer passera det här filtret, men om vi har ett jämnt talfilter kan vissa element som 3 och 137871 inte passera filtret.

Definition av gränssnitt i Go är som att definiera den karakteristiska egenskapen och mekanismen för att använda gränssnittet som ett argument för en funktion är som ett filter som upptäcker att elementet är medlem i vår önskade uppsättning eller inte. Låt oss beskriva denna aspekt med kod:

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
}

Den karakteristiska egenskapen till Number är alla strukturer som har IsNumber metoden, för NaturalNumber är alla de som har IsNumber och IsNaturalNumber metoder och slutligen för EvenNumber är alla typer som har IsNumber , IsNaturalNumber och IsEvenNumber metoder. Tack vare denna tolkning av gränssnittet kan vi lätt förstå att eftersom interface{} inte har någon karakteristisk egenskap, accepterar alla typer (eftersom det inte har något filter för att skilja mellan värden).



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow