Ir a notas de combate de idiomas (9) | Ir a la interfaz

La interfaz es una especie de convención, es un tipo abstracto, diferente de los tipos concretos que vemos como int, map, slice, etc. El tipo específico, podemos saber qué es y qué se puede hacer con él; pero la interfaz es diferente, la interfaz es abstracta, solo tiene un conjunto de métodos de interfaz, no conocemos su implementación interna, así que no No sabemos qué es la interfaz, pero sabemos lo que podemos hacer con los métodos que proporciona.

La abstracción es la ventaja de la interfaz. No necesita estar vinculada a los detalles de implementación específicos. Solo necesitamos definir la interfaz y decirle a los codificadores lo que puede hacer, para que podamos separar la implementación específica, de modo que la codificación será más flexible y adaptable, también será muy fuerte.

func main() {
	var b bytes.Buffer
	fmt.Fprint(&b,"Hello World")
	fmt.Println(b.String())
}
   

Lo anterior es un ejemplo del uso de la interfaz, primero veamos fmt.Fprintla implementación de la función.

func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
	p := newPrinter()
	p.doPrint(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
   

En el código fuente anterior, podemos ver que fmt.Fprintel primer parámetro de io.Writerla función es esta interfaz, por lo que siempre que se implemente el tipo específico de esta interfaz , se puede pasar a la fmt.Fprintfunción como parámetro y bytes.Bufferla io.Writerinterfaz se implementa , por lo que se puede pasar como un parámetro para fmt.Fprintfuncionar.

Realización interna

Mencionamos anteriormente que la interfaz se usa para definir el tipo de comportamiento, es abstracto, estos comportamientos definidos no son implementados directamente por la interfaz, sino por tipos definidos por el usuario a través de métodos. Si el tipo definido por el usuario implementa todos los métodos declarados por el tipo de interfaz, entonces el tipo definido por el usuario implementa la interfaz, por lo que el valor del tipo definido por el usuario se puede asignar al valor del tipo de interfaz.

func main() {
	var b bytes.Buffer
	fmt.Fprint(&b, "Hello World")
	var w io.Writer
	w = &b
	fmt.Println(w)
}
   

En este ejemplo, debido a que bytes.Bufferla interfaz está implementada io.Writer, podemos w = &basignar un valor.Esta operación de asignación almacenará el valor del tipo definido en el valor del tipo de interfaz.

Una vez ejecutada la operación de asignación, si llamamos al método de interfaz, en realidad está llamando al método correspondiente del tipo definido por el usuario almacenado. Aquí podemos llamar al tipo definido por el usuario 实体类型.

Podemos definir muchos tipos y dejar que implementen una interfaz. Luego estos tipos se pueden asignar a esta interfaz. En este momento, la llamada del método de la interfaz es en realidad 实体类型la llamada del método correspondiente, que es el polimorfismo.

func main() {
	var a animal

	var c cat
	a=c
	a.printInfo()
	//使用另外一个类型赋值
	var d dog
	a=d
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int
type dog int

func (c cat) printInfo(){
	fmt.Println("a cat")
}

func (d dog) printInfo(){
	fmt.Println("a dog")
}
   

El ejemplo anterior demuestra un polimorfismo. Definimos una interfaz animal, luego definimos dos tipos cate dogimplementamos la interfaz animal. Cuando está en uso, el tipo de cada catvalor c, el tipo dogdel valor dasignado a la interfaz animalvalor a, respectivamente, y a continuación, realizar ael printInfométodo, de salida diferente puede ser visto.

a cat
a dog
   

Veamos el diseño interno del valor de la interfaz después de que se asigna el valor de la interfaz. El valor de la interfaz es una estructura de datos de la longitud de la palabra, la primera palabra de la tabla contiene un puntero a la estructura interna, esta tabla interna tiene 实体类型información almacenada y metodologías asociadas; la segunda palabra se incluye en un puntero al 实体类型valor almacenado . Entonces, la estructura de valor de la interfaz es en realidad dos punteros, lo que también muestra que la interfaz es en realidad un tipo de referencia.

Conjunto de métodos

Todos sabemos que si quieres implementar una interfaz, debes implementar todos los métodos proporcionados por esta interfaz, pero al implementar métodos, podemos usar receptores de puntero para implementar, o receptores de valor. Hay una diferencia entre los dos. analizar la diferencia entre los dos.

func main() {
	var c cat
	//值作为参数传递
	invoke(c)
}
//需要一个animal接口作为参数
func invoke(a animal){
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int

//值接收者实现animal接口
func (c cat) printInfo(){
	fmt.Println("a cat")
}
   

Se cambia el ejemplo original y invokese agrega una función. La función recibe un animalparámetro de tipo de interfaz. En el ejemplo, cuando se pasa el parámetro, también se pasa por catel valor del tipo c, y el programa en ejecución se puede ejecutar normalmente. Ahora hagamos una pequeña transformación y usemos catpunteros de tipos &ccomo parámetros.

func main() {
	var c cat
	//指针作为参数传递
	invoke(&c)
}
   

Solo modifique este, los demás permanecen sin cambios, ejecutamos el programa y encontramos que se puede ejecutar normalmente. A través de este ejemplo, podemos sacar la conclusión de que cuando un tipo de entidad implementa una interfaz como receptor de valor, ya sea el valor del tipo de entidad o el puntero del valor del tipo de entidad, se implementa la interfaz .

Intentemos cambiar el receptor a un puntero.

func main() {
	var c cat
	//值作为参数传递
	invoke(c)
}
//需要一个animal接口作为参数
func invoke(a animal){
	a.printInfo()
}

type animal interface {
	printInfo()
}

type cat int

//指针接收者实现animal接口
func (c *cat) printInfo(){
	fmt.Println("a cat")
}
   

En este ejemplo, el receptor que implementa la interfaz se cambia a un puntero, pero al pasar los parámetros, todavía pasamos por valor. Haga clic para ejecutar el programa y aparecerá el siguiente mensaje de excepción:

./main.go:10: cannot use c (type cat) as type animal in argument to invoke:
	cat does not implement animal (printInfo method has pointer receiver)
1
2
 

El indicador ya nos ha dicho claramente que la interfaz catno animalestá implementada porque el printInfométodo tiene un receptor de puntero, por lo que catel valor del tipo cno se puede usar como animalparámetro del tipo de interfaz . Modifiquémoslo un poco más abajo y pasémoslo como parámetro.

func main() {
	var c cat
	//指针作为参数传递
	invoke(&c)
}
   

Todo lo demás es igual, simplemente cambie el parámetro que usó el valor antes para usar el puntero como parámetro, ejecutamos el programa nuevamente y puede ejecutarse normalmente. Se puede ver que cuando un tipo de entidad implementa una interfaz con un receptor de puntero, solo se considera que un puntero a este tipo implementa la interfaz.

Ahora resumimos estas dos reglas, primero desde la perspectiva de si el receptor del método es un valor o un puntero.

Receptores de métodos Valores
(t T) T y * T
(t * T) * T

La tabla anterior se puede interpretar como: si es un receptor de valor, tanto el valor como el puntero del tipo de entidad pueden implementar la interfaz correspondiente; si es un receptor de puntero, solo el puntero del tipo puede implementar la interfaz correspondiente.

En segundo lugar, analizamos si el tipo de entidad es un valor o un puntero.

Valores Receptores de métodos
T (t T)
* T (t T) y (t * T)

La tabla anterior se puede interpretar como: el valor del tipo solo puede realizar la interfaz del receptor de valor; el puntero al tipo puede realizar la interfaz del receptor de valor y la interfaz del receptor de puntero.

 

Ver: https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html

Supongo que te gusta

Origin blog.csdn.net/qq_32907195/article/details/112394738
Recomendado
Clasificación