We know that implicit type conversion is not allowed in the Go language, that is to say, =
variables with different types are not allowed to appear on both sides.
类型转换
, 类型断言
the essence is to convert one type into another type. The difference is that type assertions operate on interface variables.
type conversion
For 类型转换
, the two types before and after conversion must be compatible with each other. The syntax for type conversion is:
<result type> := <target type> ( <expression> )
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)
}
In the above code, I defined a variable of int
type and float64
type and tried to convert each other before them. The result was successful: int
type and float64
are compatible with each other.
If I uncomment the last line of code, the compiler will report a type incompatibility error:
cannot convert i (type int) to type []int
affirmation
As mentioned before, because the empty interface interface{}
does not define any functions, all types in Go implement the empty interface. When the formal parameter of a function is interface{}
, then in the function, an assertion needs to be made on the formal parameter to obtain its true type.
The syntax for assertions is:
<value of target type>, <Boolean parameter> := <expression>.(target type) // Safe type assertion <
value of target type> := <expression>.(target type) //Unsafe type assertion
Type conversion is somewhat similar to type assertion. The difference is that type assertion operates on the interface.
Let’s look at a brief example:
package main
import "fmt"
type Student struct {
Name string
Age int
}
func main() {
var i interface{} = new(Student)
s := i.(Student)
fmt.Println(s)
}
Run it:
panic: interface conversion: interface {
} is *main.Student, not main.Student
Directly panic
, this is because i
it is *Student
of type, not Student
of type, and the assertion fails. This happened directly panic
. Online code may not be suitable for this. You can use the "safety assertion" syntax:
func main() {
var i interface{} = new(Student)
s, ok := i.(Student)
if ok {
fmt.Println(s)
}
}
This way, even if the assertion fails, it won't panic
.
There is actually another form of assertion, which is used to switch
determine the type of the interface using statements. Each case
will be considered sequentially. When a is hit , the statements in case
will be executed , so the order of statements is very important, because there may be multiple matches.case
case
case
The code example is as follows:
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
There are three different lines of declarations in the function. Run one line each time and comment the other two lines to get three sets of running results:
// --- 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>
For the first line of statement:
var i interface{} = new(Student)
i
It is a *Student
type, matching the third case. Judging from the three printed addresses, the variables in these three places are actually different. main
There is a local variable in the function ; i
when calling the function, a copy of the parameters is actually copied, so there is another variable in the function v
, which is i
a copy of; after the assertion, a new copy is generated. So the addresses of the three variables finally printed are different.
For the second line of statements:
var i interface{} = (*Student)(nil)
What I want to explain here is that i
the dynamic type here is (*Student)
, the data is nil
, its type is not nil
, and when it nil
is compared with, the result obtained is also false
.
The last line of statement:
var i interface{}
This time i
is nil
the type.
[Extension 1]
fmt.Println
The parameters of the function are interface
. For built-in types, the function will use an exhaustive method to find out its true type, and then convert it to a string for printing. For a custom type, first determine whether the type implements String()
the method. If it does, the result of the method will be printed directly String()
; otherwise, the members of the object will be traversed through reflection for printing.
Let’s look at a short example again, it’s relatively simple, don’t be nervous:
package main
import "fmt"
type Student struct {
Name string
Age int
}
func main() {
var s = Student{
Name: "qcrao",
Age: 18,
}
fmt.Println(s)
}
Because Student
the structure does not implement String()
the method, fmt.Println
reflection will be used to print the member variables one by one:
{
qcrao 18}
Add an String()
implementation of the method:
func (s Student) String() string {
return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}
Print the result:
[Name: qcrao], [Age: 18]
Print according to our customized method.
[Extension 2]
Regarding the above example, if you change it:
func (s *Student) String() string {
return fmt.Sprintf("[Name: %s], [Age: %d]", s.Name, s.Age)
}
Note that the receiver types of the two functions are different. Now Student
the structure only has one function with the receiver type. Print the result 指针类型
:String()
{
qcrao 18}
Why?
A type has only methods whose
T
receivers are and ; a type has methods whose receivers are and . Syntactically, the methods that can be directly adjusted are just syntactic sugar for .T
*T
T
*T
T
*T
Go
Therefore, when Student
the structure defines String()
a method whose receiver type is a value type, pass
fmt.Println(s)
fmt.Println(&s)
All can be printed in customized formats.
If Student
the structure defines String()
a method whose receiver type is a pointer type, it can only be passed
fmt.Println(&s)
to print in a custom format.