В чем смысл интерфейса {}?
Я новичок в интерфейсах и пытаюсь сделать 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 устанавливает оба слова значения интерфейса.
Первое слово в значении интерфейса указывает на то, что я называю интерфейсной таблицей или 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
}
Здесь уже есть хорошие ответы. Позвольте мне добавить свое тоже для тех, кто хочет понять это интуитивно:
Интерфейс
Вот интерфейс с одним методом:
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"
Как видите, в этом нет ничего загадочного, но злоупотреблять им очень легко. Держитесь подальше от этого, насколько вы можете.
Он называется пустым интерфейсом и реализуется всеми типами, что означает, что вы можете поместить в 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.
Тип реализует любой интерфейс, содержащий любое подмножество его методов, и поэтому может реализовывать несколько отдельных интерфейсов. Например, все типы реализуют пустой интерфейс:
интерфейс{}
Концепции для захвата:
- У всего есть Тип. Вы можете определить новый тип, назовем его T. Допустим, теперь наш тип
T
имеет 3 метода:A
,B
,C
, - Набор методов, указанных для типа, называется "тип интерфейса". Давайте назовем это в нашем примере: T_interface. Равно
T_interface = (A, B, C)
- Вы можете создать "тип интерфейса", определив сигнатуру методов.
MyInterface = (A, )
- Когда вы указываете переменную типа"тип интерфейса", вы можете назначать ей только те типы, которые имеют интерфейс, который является надмножеством вашего интерфейса. Это означает, что все методы, содержащиеся в
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{} просто не имеет объявления метода , поэтому он может принимать любой тип