В чем смысл интерфейса {}?

Я новичок в интерфейсах и пытаюсь сделать SOAP-запрос от github

Я не понимаю смысла

Msg interface{}

в этом коде:

type Envelope struct {
    Body `xml:"soap:"`
}

type Body struct {
    Msg interface{}
}

Я наблюдал тот же синтаксис в

fmt.Println

но не понимаю, что достигается

interface{}

8 ответов

Решение

Вы можете обратиться к статье " Как использовать интерфейсы в Go " (на основе " описания интерфейсов Расс Кокса "):

Что такое интерфейс?

Интерфейс это две вещи:

  • это набор методов,
  • но это тоже тип

interface{} типа, пустой интерфейс - это интерфейс, который не имеет методов.

Поскольку ключевое слово Implements не существует, все типы реализуют как минимум нулевые методы, и удовлетворение интерфейса выполняется автоматически, все типы удовлетворяют пустому интерфейсу.
Это означает, что если вы напишите функцию, которая принимает interface{} значение в качестве параметра, вы можете предоставить этой функции любое значение.

(Что это Msg представляет в вашем вопросе: любое значение)

func DoSomething(v interface{}) {
   // ...
}

Вот где это сбивает с толку:

внутри DoSomething функция, что есть v тип?

Начинающие суслики склонны считать, что " v любого типа ", но это неправильно.
v не любого типа; это из interface{} тип.

При передаче значения в DoSomething Во время выполнения Go среда выполнения (если необходимо) выполнит преобразование значения в interface{} значение
Все значения имеют ровно один тип во время выполнения, и v один статический тип interface{},

Значение интерфейса состоит из двух слов данных:

  • одно слово используется для указания на таблицу методов для базового типа значения,
  • а другое слово используется для указания на фактические данные, хранящиеся в этом значении.

Приложение: Это была статья Русса, в которой описана структура интерфейса:

type Stringer interface {
    String() string
}

Значения интерфейса представлены в виде пары из двух слов, дающей указатель на информацию о типе, хранимом в интерфейсе, и указатель на связанные данные.
Присвоение b значению интерфейса типа Stringer устанавливает оба слова значения интерфейса.

http://research.swtch.com/gointer2.png

Первое слово в значении интерфейса указывает на то, что я называю интерфейсной таблицей или itable (произносится как i-таблица; в источниках времени выполнения имя реализации C - Itab).
Itable начинается с некоторых метаданных о задействованных типах, а затем становится списком указателей на функции.
Обратите внимание, что itable соответствует типу интерфейса, а не динамическому типу.
С точки зрения нашего примера, это можно Stringer Тип Binary содержит список методов, используемых для удовлетворения Stringer, который просто String: Другие методы Binary (Get) не появляться в itable,

Второе слово в значении интерфейса указывает на фактические данные, в данном случае это копия b,
Назначение var s Stringer = b делает копию b а не указывать на b по той же причине, что var c uint64 = b делает копию: если b последующие изменения, s а также c должны иметь первоначальное значение, а не новое.
Значения, хранимые в интерфейсах, могут быть произвольно большими, но только одно слово выделено для хранения значения в структуре интерфейса, поэтому назначение выделяет кусок памяти в куче и записывает указатель в слот из одного слова.

interface{} означает, что вы можете указать значение любого типа, включая ваш собственный тип. Все типы в Go удовлетворяют пустой интерфейс (interface{} это пустой интерфейс).
В вашем примере поле Msg может иметь значение любого типа.

Пример:

package main

import (
    "fmt"
)

type Body struct {
    Msg interface{}
}

func main() {
    b := Body{}
    b.Msg = "5"
    fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
    b.Msg = 5

    fmt.Printf("%#v %T", b.Msg, b.Msg) //Output:  5 int
}

Go Playground

Здесь уже есть хорошие ответы. Позвольте мне добавить свое тоже для тех, кто хочет понять это интуитивно:


Интерфейс

Вот интерфейс с одним методом:

type Runner interface {
    Run()
}

Так что любой тип, который имеет Run() Метод удовлетворяет интерфейсу Runner:

type Program struct {
    /* fields */
}

func (p Program) Run() {
    /* running */
}

func (p Program) Stop() {
    /* stopping */
}
  • Хотя тип Program также имеет метод Stop, он по-прежнему удовлетворяет интерфейсу Runner, поскольку все, что требуется, - это иметь все методы интерфейса для его удовлетворения.

  • Итак, у него есть метод Run, и он удовлетворяет интерфейсу Runner.


Пустой интерфейс

Вот именованный пустой интерфейс без каких-либо методов:

type Empty interface {
    /* it has no methods */
}

Таким образом, любой тип удовлетворяет этому интерфейсу. Потому что для удовлетворения этого интерфейса не требуется никакого метода. Например:

// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty

a = 5
a = 6.5
a = "hello"

Но удовлетворяет ли приведенный выше тип программы? Да:

a = Program{} // ok

interface {} равен интерфейсу Empty выше.

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

Как видите, в этом нет ничего загадочного, но злоупотреблять им очень легко. Держитесь подальше от этого, насколько вы можете.


https://play.golang.org/p/A-vwTddWJ7G

Он называется пустым интерфейсом и реализуется всеми типами, что означает, что вы можете поместить в Msg поле.

Пример:

body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}

body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}

body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}

Это логическое расширение того факта, что тип реализует интерфейс, как только у него есть все методы интерфейса.

Из спецификации Голанга:

Тип интерфейса определяет набор методов, называемый его интерфейсом. Переменная типа интерфейса может хранить значение любого типа с набором методов, который является любым надмножеством интерфейса. Такой тип, как говорят, реализует интерфейс. Значение неинициализированной переменной типа интерфейса равно nil.

Тип реализует любой интерфейс, содержащий любое подмножество его методов, и поэтому может реализовывать несколько отдельных интерфейсов. Например, все типы реализуют пустой интерфейс:

интерфейс{}

Концепции для захвата:

  1. У всего есть Тип. Вы можете определить новый тип, назовем его T. Допустим, теперь наш тип T имеет 3 метода: A, B, C,
  2. Набор методов, указанных для типа, называется "тип интерфейса". Давайте назовем это в нашем примере: T_interface. Равно T_interface = (A, B, C)
  3. Вы можете создать "тип интерфейса", определив сигнатуру методов. MyInterface = (A, )
  4. Когда вы указываете переменную типа"тип интерфейса", вы можете назначать ей только те типы, которые имеют интерфейс, который является надмножеством вашего интерфейса. Это означает, что все методы, содержащиеся в MyInterface должны содержаться внутри T_interface

Вы можете сделать вывод, что все "типы интерфейса" всех типов являются надмножеством пустого интерфейса.

Пример, расширяющий отличный ответ @VonC и комментарий @NickCraig-Wood. interface{} может указывать на что угодно, и для его использования вам потребуется утверждение приведения / типа.

package main

import (
    . "fmt"
    "strconv"
)

var c = cat("Fish")
var d = dog("Bone")

func main() {
    var i interface{} = c
    switch i.(type) {
    case cat:
        c.Eat() // Fish
    }

    i = d
    switch i.(type) {
    case dog:
        d.Eat() // Bone
    }

    i = "4.3"
    Printf("%T %v\n", i, i) // string 4.3
    s, _ := i.(string)      // type assertion
    f, _ := strconv.ParseFloat(s, 64)
    n := int(f)             // type conversion
    Printf("%T %v\n", n, n) // int 4
}

type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }

i переменная пустого интерфейса со значением cat("Fish"). Допустимо создание значения метода из значения типа интерфейса. См. https://golang.org/ref/spec.

Переключатель типа подтверждает i тип интерфейса cat("Fish"). См. https://golang.org/doc/effective_go.html.i затем переназначается на dog("Bone"). Переключатель типа подтверждает, чтоi тип интерфейса изменен на dog("Bone").

Вы также можете попросить компилятор проверить, что тип T реализует интерфейс I при попытке выполнения задания: var _ I = T{}. См. https://golang.org/doc/faq и /questions/45230203/ubedites-chto-tip-realizuet-interfejs-vo-vremya-kompilyatsii-v-go/55349460#55349460.

Все типы реализуют пустой интерфейс interface{}. См. https://talks.golang.org/2012/goforc.slide и https://golang.org/ref/spec. В этом примереi переназначен, на этот раз строке "4.3".i затем присваивается новой строковой переменной s с участием i.(string) перед s преобразуется в тип float64 f с помощью strconv. в заключениеf конвертируется в nтип int, равный 4. См. В чем разница между преобразованием типа и утверждением типа?

Встроенные карты и срезы Go, а также возможность использовать пустой интерфейс для создания контейнеров (с явной распаковкой) означают, что во многих случаях можно написать код, который делает то, что позволяют дженерики, хотя и менее плавно. См. https://golang.org/doc/faq.

Интерфейс представляет собой тип, подобный структуре, но не содержит никакой реализации. Это контракт между объектом и типом структуры для удовлетворения общей функциональности или общей функциональности, действующей на различные типы структурных объектов, например, в приведенном ниже коде PrintDetails является общей функциональностью действует на различные типы структур в качестве инженера, менеджера, старшего руководителя, пожалуйста, найдите пример кода интерфейса, примерhttps://play.golang.org/p/QnAqEYGiiF7

  • Метод может быть привязан к любому типу (int , string, pointer и т. д.) в GO .

  • Интерфейс - это способ указать, какой метод должен иметь один тип, если тип реализует эти методы, он может быть назначен этому интерфейсу.

  • Interface{} просто не имеет объявления метода , поэтому он может принимать любой тип

Другие вопросы по тегам