¿Cuál es el significado de interfaz{}?
Soy nuevo en las interfaces y estoy intentando realizar una solicitud SOAP mediante github.
No entiendo el significado de
Msg interface{}
en este código:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
He observado la misma sintaxis en
fmt.Println
pero no entiendo lo que se está logrando con
interface{}
Nota: Go 1.18 (primer trimestre de 2022) cambia el nombre interface{}
a any
(alias de interface{}
).
Consulte el problema 49884 , CL 368254 y confirme 2580d0e .
Vea la última parte de esta respuesta.
Puede consultar el artículo " Cómo utilizar interfaces en Go " (basado en la " Descripción de interfaces de Russ Cox "):
¿ Qué es una interfaz?
Una interfaz es dos cosas:
- es un conjunto de métodos,
- pero también es un tipo
El
interface{}
tipo (oany
con Go 1.18+), la interfaz vacía es la interfaz que no tiene métodos.Dado que no existe una palabra clave implements, todos los tipos implementan al menos cero métodos y la satisfacción de una interfaz se realiza automáticamente, por lo tanto, todos los tipos satisfacen la interfaz vacía .
Eso significa que si escribe una función que toma uninterface{}
valor como parámetro, puede proporcionarle cualquier valor a esa función .
(Eso es lo que Msg
representa en tu pregunta: cualquier valor)
func DoSomething(v interface{}) {
// ...
}
func DoSomething(v any) {
// ...
}
Aquí es donde se vuelve confuso:
dentro de la
DoSomething
función, ¿ cuál esv
el tipo?A los tuzas principiantes se les hace creer que “
v
es de cualquier tipo”, pero eso está mal.
v
no es de ningún tipo; es deinterface{}
tipo .Al pasar un valor a la
DoSomething
función, el tiempo de ejecución de Go realizará una conversión de tipo (si es necesario) y convertirá el valor en uninterface{}
valor .
Todos los valores tienen exactamente un tipo en tiempo de ejecución, yv
el tipo estático esinterface{}
(oany
con Go 1.18+).Un valor de interfaz se construye con dos palabras de datos :
- se usa una palabra para señalar una tabla de métodos para el tipo subyacente del valor,
- y la otra palabra se usa para señalar los datos reales que contiene ese valor.
Anexo: aquí es donde el artículo de Russ es bastante completo con respecto a la estructura de la interfaz:
type Stringer interface {
String() string
}
Los valores de la interfaz se representan como un par de dos palabras que dan un puntero a información sobre el tipo almacenado en la interfaz y un puntero a los datos asociados.
Asignar b a un valor de interfaz de tipo Stringer establece ambas palabras del valor de interfaz.La primera palabra en el valor de la interfaz apunta a lo que yo llamo una tabla de interfaz o itable (se pronuncia i-table; en las fuentes de tiempo de ejecución, el nombre de la implementación en C es Itab).
La itable comienza con algunos metadatos sobre los tipos involucrados y luego se convierte en una lista de punteros de función.
Tenga en cuenta que el itable corresponde al tipo de interfaz, no al tipo dinámico .
En términos de nuestro ejemplo, el itable paraStringer
contener el tipo Binary enumera los métodos utilizados para satisfacer Stringer, que es simplementeString
: Los otros métodos de Binary (Get
) no aparecen en el archivoitable
.La segunda palabra en el valor de la interfaz apunta a los datos reales , en este caso una copia de
b
.
La asignaciónvar s Stringer = b
hace una copiab
en lugar de señalarb
por la misma razón quevar c uint64 = b
hace una copia: sib
luego cambia,s
yac
se supone que debe tener el valor original, no el nuevo.
Los valores almacenados en las interfaces pueden ser arbitrariamente grandes, pero solo se dedica una palabra a contener el valor en la estructura de la interfaz, por lo que la asignación asigna una porción de memoria en el montón y registra el puntero en la ranura de una palabra.
El problema 33232 parece señalarse any
como un alias interface{}
en Go 1.18 (primer trimestre de 2022)
Russ Cox explica :
'
any
' estar solo para restricciones es un detalle que estará en cada artículo sobre genéricos: libros, publicaciones de blogs, etc. Si creemos que es probable que eventualmente lo permitamos, tiene sentido permitirlo desde el principio y evitar invalidar todo ese material escrito.'
any
' Ser sólo para restricciones es un recorte inesperado que reduce la generalidad y la ortogonalidad de los conceptos. Es fácil decir "esperemos y veremos", pero la prescripción de usos tiende a crear características mucho más irregulares que la generalidad total. También vimos esto con alias de tipo (y afortunadamente resistimos casi todos los recortes propuestos).Si "
any
" está permitido en códigos genéricos pero no en códigos no genéricos, entonces podría alentar a las personas a abusar de los genéricos simplemente porque "any
" es mejor de escribir que "interface{}
", cuando la decisión sobre genéricos o no realmente debería tomarse considerando otros factores.Si permitimos '
any
' también para el uso ordinario no genérico, entonces verinterface{}
en el código podría servir como una especie de señal de que el código es anterior a los genéricos y aún no ha sido reconsiderado en el mundo posgenérico. Parte del código utilizadointerface{}
debería utilizar genéricos. Otro código debería seguir utilizando interfaces.
Reescribirlo de una forma u otra para eliminar el texto "interface{}
" daría a las personas una forma clara de ver lo que habían actualizado y lo que no. (Por supuesto, algún código que podría ser mejor con genéricos aún debe usarseinterface{}
por razones de compatibilidad con versiones anteriores, pero aún se puede actualizar para confirmar que la decisión fue considerada y tomada).
Ese hilo también incluye una explicación sobreinterface{}
:
No es un diseño especial, sino una consecuencia lógica de la sintaxis de declaración de tipos de Go.
Puede utilizar interfaces anónimas con más de cero métodos:
func f(a interface{Foo(); Bar()}) { a.Foo() a.Bar() }
De manera análoga a cómo se pueden usar estructuras anónimas en cualquier lugar donde se espere un tipo:
func f(a struct{Foo int; Bar string}) { fmt.Println(a.Foo) fmt.Println(a.Bar) }
Una interfaz vacía coincide con todos los tipos porque todos los tipos tienen al menos cero métodos.
Eliminar
interface{}
significaría eliminar toda la funcionalidad de la interfaz del idioma si desea mantener la coherencia o no desea introducir un caso especial.
interface{}
significa que puede poner valores de cualquier tipo, incluido su propio tipo personalizado. Todos los tipos en Go satisfacen una interfaz vacía ( interface{}
es una interfaz vacía).
En su ejemplo, el campo Mensaje puede tener valores de cualquier tipo.
Ejemplo:
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
}
ir al patio de recreo
Ya hay buenas respuestas aquí. Permítanme agregar el mío también para otros que quieran entenderlo intuitivamente:
Interfaz
Aquí hay una interfaz con un método:
type Runner interface {
Run()
}
Entonces, cualquier tipo que tenga un Run()
método satisface la interfaz Runner:
type Program struct {
/* fields */
}
func (p Program) Run() {
/* running */
}
func (p Program) Stop() {
/* stopping */
}
Aunque el tipo Programa también tiene un método Stop, aún satisface la interfaz Runner porque todo lo que se necesita es tener todos los métodos de una interfaz para satisfacerla.
Entonces, tiene un método Run y satisface la interfaz Runner.
Interfaz vacía
Aquí hay una interfaz vacía con nombre sin ningún método:
type Empty interface {
/* it has no methods */
}
Entonces cualquier tipo satisface esta interfaz. Porque no se necesita ningún método para satisfacer esta interfaz. Por ejemplo:
// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty
a = 5
a = 6.5
a = "hello"
Pero, ¿el tipo de programa anterior lo satisface? Sí:
a = Program{} // ok
interface{} es igual a la interfaz vacía anterior.
var b interface{}
// true: a == b
b = a
b = 9
b = "bye"
Como ves, no tiene nada de misterioso, pero es muy fácil abusar de él. Manténgase alejado de él tanto como pueda.
https://play.golang.org/p/A-vwTddWJ7G