Sabemos que la conversión de tipos implícita no está permitida en el lenguaje Go, es decir, =
no se permite que aparezcan variables con diferentes tipos en ambos lados.
类型转换
, 类型断言
la esencia es convertir un tipo en otro tipo. La diferencia es que las afirmaciones de tipo operan sobre variables de interfaz.
conversión de tipo
Para 类型转换
, los dos tipos antes y después de la conversión deben ser compatibles entre sí. La sintaxis para la conversión de tipos es:
<tipo de resultado> := <tipo de destino> ( <expresión> )
package main
import "fmt"
func main() {
var i int = 9
var f float64
f = float64(i)
fmt.Printf("%T, %v\n", f, f)
f = 10.8
a := int(f)
fmt.Printf("%T, %v\n", a, a)
// s := []int(i)
}
En el código anterior, definí una variable de int
tipo y float64
tipo e intenté convertirla entre sí antes que ellas, pero el resultado fue exitoso: int
tipo y float64
son compatibles entre sí.
Si descomento la última línea de código, el compilador informará un error de incompatibilidad de tipos:
cannot convert i (type int) to type []int
afirmación
Como se mencionó anteriormente, debido a que la interfaz vacía interface{}
no define ninguna función, todos los tipos en Go implementan la interfaz vacía. Cuando el parámetro formal de una función es interface{}
, entonces en la función, se debe hacer una afirmación sobre el parámetro formal para obtener su tipo verdadero.
La sintaxis de las afirmaciones es:
<valor del tipo de destino>, <parámetro booleano> := <expresión>.(tipo de destino) // Aserción de tipo seguro <
valor del tipo de destino> := <expresión>.(tipo de destino) //Aserción de tipo inseguro
La conversión de tipos es algo similar a la aserción de tipo, la diferencia es que la aserción de tipo opera en la interfaz.
Veamos un breve ejemplo:
package main
import "fmt"
type Student struct {
Name string
Age int
}
func main() {
var i interface{} = new(Student)
s := i.(Student)
fmt.Println(s)
}
Ejecutarlo:
panic: interface conversion: interface {
} is *main.Student, not main.Student
Directamente panic
, esto se debe a i
que es *Student
de tipo, no Student
de tipo, y la aserción falla. Esto sucedió directamente panic
. Es posible que el código en línea no sea adecuado para esto. Puede utilizar la sintaxis de "aserción de seguridad":
func main() {
var i interface{} = new(Student)
s, ok := i.(Student)
if ok {
fmt.Println(s)
}
}
De esta manera, incluso si la afirmación falla, no lo hará panic
.
En realidad, existe otra forma de aserción, que se utiliza para switch
determinar el tipo de interfaz mediante declaraciones. Cada uno case
será considerado secuencialmente. Cuando se golpea a , case
se ejecutarán case
las declaraciones en , por lo que case
el orden de las declaraciones es muy importante, porque puede haber varias case
coincidencias.
El ejemplo de código es el siguiente:
func main() {
//var i interface{} = new(Student)
//var i interface{} = (*Student)(nil)
var i interface{}
fmt.Printf("%p %v\n", &i, i)
judge(i)
}
func judge(v interface{}) {
fmt.Printf("%p %v\n", &v, v)
switch v := v.(type) {
case nil:
fmt.Printf("%p %v\n", &v, v)
fmt.Printf("nil type[%T] %v\n", v, v)
case Student:
fmt.Printf("%p %v\n", &v, v)
fmt.Printf("Student type[%T] %v\n", v, v)
case *Student:
fmt.Printf("%p %v\n", &v, v)
fmt.Printf("*Student type[%T] %v\n", v, v)
default:
fmt.Printf("%p %v\n", &v, v)
fmt.Printf("unknow\n")
}
}
type Student struct {
Name string
Age int
}
main
Hay tres líneas diferentes de declaraciones en la función. Ejecute una línea cada vez y comente las otras dos líneas para obtener tres conjuntos de resultados de ejecución:
// --- var i interface{
} = new(Student)
0xc4200701b0 [Name: ], [Age: 0]
0xc4200701d0 [Name: ], [Age: 0]
0xc420080020 [Name: ], [Age: 0]
*Student type[*main.Student] [Name: ], [Age: 0]
// --- var i interface{
} = (*Student)(nil)
0xc42000e1d0 <nil>
0xc42000e1f0 <nil>
0xc42000c030 <nil>
*Student type[*main.Student] <nil>
// --- var i interface{
}
0xc42000e1d0 <nil>
0xc42000e1e0 <nil>
0xc42000e1f0 <nil>
nil type[<nil>] <nil>
Para la primera línea de declaración:
var i interface{} = new(Student)
i
Es un *Student
tipo que coincide con el tercer caso. A juzgar por las tres direcciones impresas, las variables en estos tres lugares son en realidad diferentes. main
Hay una variable local en la función ; i
al llamar a la función, en realidad se copia una copia de los parámetros, por lo que hay otra variable en la función v
, que es i
una copia de; después de la afirmación, se genera una nueva copia. Entonces las direcciones de las tres variables finalmente impresas son diferentes.
Para la segunda línea de declaraciones:
var i interface{} = (*Student)(nil)
Lo que quiero explicar aquí es que i
el tipo dinámico aquí es (*Student)
, los datos son nil
, su tipo no nil
, y cuando se nil
compara con, el resultado obtenido también es false
.
La última línea de declaración:
var i interface{}
Esta vez i
es nil
el tipo.
[Extensión 1]
fmt.Println
Los parámetros de la función son interface
. Para los tipos integrados, la función utilizará un método exhaustivo para descubrir su tipo verdadero y luego lo convertirá en una cadena para imprimir. Para un tipo personalizado, primero determine si el tipo implementa String()
el método. Si lo hace, el resultado del método se imprimirá directamente String()
; de lo contrario, los miembros del objeto se atravesarán mediante reflexión para imprimir.
Veamos nuevamente un ejemplo breve, es relativamente simple, no te pongas nervioso:
package main
import "fmt"
type Student struct {
Name string
Age int
}
func main() {
var s = Student{
Name: "qcrao",
Age: 18,
}
fmt.Println(s)
}
Debido a que Student
la estructura no implementa String()
el método, fmt.Println
se utilizará la reflexión para imprimir las variables miembro una por una:
{
qcrao 18}
Agregue una String()
implementación del método:
func (s Student) String() string {
return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}
Resultado de impresión:
[Name: qcrao], [Age: 18]
Imprima según nuestro método personalizado.
[Extensión 2]
Respecto al ejemplo anterior, si lo cambia:
func (s *Student) String() string {
return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}
Tenga en cuenta que los tipos de receptor de las dos funciones son diferentes. Ahora Student
la estructura solo tiene una función con el tipo de receptor. Imprima el resultado 指针类型
:String()
{
qcrao 18}
¿Por qué?
Un tipo tiene sólo métodos cuyos
T
receptores son y ; un tipo tiene métodos cuyos receptores son y . Sintácticamente, los métodos que se pueden ajustar directamente son solo azúcar sintáctico para.T
*T
T
*T
T
*T
Go
Por lo tanto, cuando Student
la estructura define String()
un método cuyo tipo de receptor es un tipo de valor, pase
fmt.Println(s)
fmt.Println(&s)
Todo se puede imprimir en formatos personalizados.
Si Student
la estructura define String()
un método cuyo tipo de receptor es un tipo de puntero, solo se puede pasar
fmt.Println(&s)
para imprimir en un formato personalizado.