1. Definición de la interfaz
Una interfaz es un acuerdo con la persona que llama, es un tipo muy abstracto y no necesita estar vinculado a detalles de implementación específicos. Interfaz de hacer es definir buena convención, informe a la persona que llama lo que pueden hacer, pero no sé su aplicación interna, lo que hemos visto y el tipo específico, tales como int
, map
, slice
etc., no son los mismos.
Definir y estructurar la interfaz es ligeramente diferente, aunque todo para type
comenzar con la palabra clave, pero la interfaz de palabras clave interface
representa el tipo de interfaz personalizada.
Esa person
es una interfaz, que tiene dos métodos sayName() string
y sayAge() int
, como un todo, se muestra en el siguiente código:
type person interface {
sayName() string
sayAge() int
}
}
Para person
la interfaz, le dirá a la persona que llama a través de su sayName()
cadena de nombre de método de adquisición, a través de su sayAge()
método de adquisición de edad, cuál es la interfaz acordada. En cuanto a cómo se obtiene la cadena y cómo se ve, a la interfaz no le importa, y al llamador no necesita preocuparse, porque esto lo hace el implementador de la interfaz.
2. Implementación de la interfaz
La interfaz debe ser implementada por un tipo particular a student
la estructura, por ejemplo,
type student struct {
name string
age int
}
Se logra person
interfaces, como se muestra en el siguiente código:
func (s student) sayName() string {
fmt.Printf("name is %v\n", s.name)
return s.name
}
func (s student) sayAge() int {
fmt.Printf("name is %v\n", s.age)
return s.age
}
Escriba estructura para student
definir un método que en la firma del método, y una interfaz (nombre, parámetros y valores de retorno) lo mismo, por lo que la estructura student
se realiza person
interfaz.
Nota: Si una interfaz tiene varios métodos, se considera que cada método que necesita implementar la interfaz implementa la interfaz.
3. Asignación de interfaz
Asignación de interfaz Go
dividida en los siguientes dos idiomas de situaciones:
- Asigne la instancia del objeto a la interfaz;
- Asignar una interfaz a otra interfaz.
3.1 Asignar instancia de objeto a la interfaz
Primero, analice la asignación de un cierto tipo de instancia de objeto a la interfaz, que requiere que la instancia de objeto implemente todos los métodos requeridos por la interfaz, de la siguiente manera:
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
En consecuencia, definimos la interfaz de la LessAdder
siguiente manera:
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
Ahora hay una pregunta: Supongamos que definimos los Integer
tipos de instancia de un objeto, ¿cómo asignarlo a LessAdder
la interfaz?
¿Debo usar la siguiente declaración (1) o declaración (2)?
var a Integer = 1
var b LessAdder = &a ... (1)
var b LessAdder = a ... (2)
La respuesta es que debe usarse la oración (1). La razón es que el Go
lenguaje puede basarse en las siguientes funciones:
func (a Integer) Less(b Integer) bool {
return a < b
}
Genere automáticamente un nuevo Less()
método:
func (a *Integer) Less(b Integer) bool {
return (*a).Less(b)
}
Por lo tanto, el tipo *Integer
existe ambos Less()
métodos, hay Add()
métodos para cumplir con LessAdder
la interfaz.
Por otro lado, según
func (a *Integer) Add(b Integer)
Esta función no puede generar automáticamente el siguiente método de miembro:
func (a Integer) Add(b Integer) {
(&a).Add(b)
}
Debido a que el (&a).Add()
cambio es solo una función de los parámetros a, sujeto a influencias externas no es práctico de operar, lo cual no es consistente con las
expectativas de los usuarios.
Por lo tanto, el Go
idioma no genera automáticamente esta función para él. Por lo tanto, el tipo Integer
hay solo Less()
un método, la falta de Add()
un método, no satisface LessAdder
la interfaz, por lo tanto, las declaraciones anteriores (2) no se pueden asignar.
Para demostrar aún más el razonamiento anterior, es posible que deseemos definir una Lesser
interfaz de la siguiente manera:
type Lesser interface {
Less(b Integer) bool
}
Luego defina una Integer
instancia de objeto de tipo, asígnela a Lesser
la interfaz:
var a Integer = 1
var b1 Lesser = &a ... (1)
var b2 Lesser = a ... (2)
Como esperábamos, tanto la declaración (1) como la declaración (2) se pueden compilar y pasar.
3.2 Asignar una interfaz a otra interfaz
En el Go
lenguaje, siempre que ambas interfaces tengan la misma lista de métodos (no importa el orden diferente), entonces son equivalentes, se pueden asignar entre sí.
4. Llamada de interfaz
Implementa person
la interfaz después de su uso. En primer lugar, me gustaría definir una person
interfaz de función de impresión , de la siguiente manera:
func printPersonInfo(p person) {
fmt.Printf("name is %v\n", p.sayName())
fmt.Printf("age is %v\n", p.sayAge())
}
Esta función está definida printPersonInfo
, recibe un person
tipo de interfaz del parámetro y luego imprime la cadena de person
interfaz sayName()
devuelta por el método, sayAge()
el método devuelve Age
printPersonInfo
La ventaja de esta función es que está orientada a la interfaz de programación, siempre que un tipo implemente Stringer
la interfaz, pueda imprimir una cadena de caracteres correspondiente y no controle las implementaciones específicas del tipo.
Dado que student
implementa person
la interfaz, tan variable stu
en función de printPersonInfo
los parámetros, se puede imprimir de la siguiente manera:
printPersonInfo(stu)
Producción
name is wohu
age is 20
Deje que la estructura teacher
también implemente person
una interfaz, como se muestra en el siguiente código:
type teacher struct {
name string
age int
}
func (t teacher) sayName() string {
return t.name
}
func (t teacher) sayAge() int {
return t.age
}
Dado que la estructura teacher
también implementa la función de person
interfaz printPersonInfo
sin ningún cambio, se puede usar directamente, de la siguiente manera:
printPersonInfo(t)
Resultado de salida:
name is Jack
age is 40
Este es el beneficio de la interfaz orientada, siempre que la definición y la persona que llama cumplan con el acuerdo, puede usarlo, independientemente de la implementación específica. Los implementadores de la interfaz también pueden actualizar y refactorizar mejor sin ningún impacto, porque la convención de la interfaz no ha cambiado.
5. Receptor de valor y receptor de puntero
Ya sabemos que si quieres implementar una interfaz, debes implementar todos los métodos proporcionados por la interfaz, y al explicar el método en la última lección, defines un método, el cual tiene dos tipos: receptor tipo valor y receptor tipo puntero. . Se pueden invocar ambos métodos, ya que el Go
compilador realiza la conversión automáticamente, el valor del puntero de tipo y el tipo de destinatarios son equivalentes. Pero en la implementación de la interfaz, el receptor del tipo de valor y el receptor del tipo de puntero son diferentes, analizaré la diferencia entre los dos en detalle a continuación.
Lo anterior se ha verificado el tipo de estructura implementa person
una interfaz, entonces la estructura correspondiente al puntero también implementa esta interfaz. Usé el siguiente código para probar:
printPersonInfo(&s)
Resultado de salida:
name is wohu
age is 20
Después de la prueba, encontrará el t
puntero de la variable ya que un argumento pasado a printPersonInfo
la función es posible, compilar y ejecutar son normales. Esto demuestra que cuando un receptor de tipo de valor implementa una interfaz, tanto el tipo en sí como el tipo de puntero del tipo implementan la interfaz.
El receptor de ejemplo median ( s student
) implementa person
la interfaz, el tipo student
y el tipo de puntero que *student
habría logrado person
la interfaz.
Ahora, cambio el receptor a un tipo de puntero, como se muestra en el siguiente código:
func (s *student) sayName() string {
return s.name
}
func (s *student) sayAge() int {
return s.age
}
El receptor de tipo puntero modificado para encontrar la printPersonInfo(stu)
compilación de código de línea de ejemplo no se pasa, lo que sugiere los siguientes errores:
demo.go:46:17: cannot use stu (type student) as type person in argument to printPersonInfo:
student does not implement person (sayAge method has pointer receiver)
Lo que significa que el tipo student
no implementa person
la interfaz. Esto prueba que cuando un receptor de tipo puntero implementa una interfaz, solo se considera que el tipo de puntero correspondiente implementa la interfaz.
para resumir:
-
Cuando el tipo de valor como destinatario,
student
tipo y*student
tipo implementa esta interfaz; -
Cuando el puntero escribe como receptor, solo
*student
el tipo implementa esta interfaz;
Se puede encontrar que existen tipos que implementan interfaces *student
, lo que también muestra que el tipo de puntero es más versátil, no importa qué tipo de receptor, puede implementar la interfaz.
package main
import "fmt"
type student struct {
name string
age int
}
type teacher struct {
name string
age int
}
type person interface {
sayName() string
sayAge() int
}
func (t teacher) sayName() string {
return t.name
}
func (t teacher) sayAge() int {
return t.age
}
func (s *student) sayName() string {
// fmt.Printf("name is %v\n", s.name)
return s.name
}
func (s *student) sayAge() int {
// fmt.Printf("age is %v\n", s.age)
return s.age
}
func printPersonInfo(p person) {
fmt.Printf("name is %v\n", p.sayName())
fmt.Printf("age is %v\n", p.sayAge())
}
func main() {
stu := student{
name: "wohu", age: 20}
// t := teacher{name: "Jack", age: 40}
printPersonInfo(stu)
// printPersonInfo(t)
}