详解Go语言各种常见类型的默认值和判空方法
起因(解决的问题)
由于在项目中设计到了类型的判空,所以突然好奇起来,每个类型如果只是声明,而没有初始化,那么默认值是多少?怎么判断它是不是空值?所以去整理了一下
基本类型的默认值
1.常见的基本数据类型有:数据类型(int,uint,float之类的),字符串(string),结构体,数组,指针。
2.那么他们的默认值是:
数据类型
默认值
int
0
float
0.00000
string
“”
结构体
根据结构体内部的基础数据类型进行初始化赋值,下面会有demo
数组(切片)
空数组
指针
nil
3.例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package
main
import
(
"fmt"
)
type
UserInfo
struct
{
Name
string
Age
int
Sex
string
Flag
bool
}
// main函数
func
main() {
PrintDefault()
}
// 输出默认值的函数
func
PrintDefault() {
var
a
int
var
b
bool
var
c
float64
var
d
byte
var
e
string
var
f UserInfo
var
g *UserInfo
var
ip *
int
var
bp *
bool
var
fp *
float64
var
sp *
string
var
ssp *
byte
var
iArray []
int
fmt.
Println
(
"-------默认值列表--------"
)
fmt.Printf(
"int的默认值为:%d\n"
, a)
fmt.Printf(
"bool的默认值为:%t\n"
, b)
fmt.Printf(
"float64的默认值为:%f\n"
, c)
fmt.Printf(
"byte的默认值为:%b\n"
, d)
fmt.Printf(
"string的默认值为:%s\n"
, e)
fmt.Printf(
"结构体UserInfo的默认值为:%v\n"
, f)
fmt.Printf(
"结构体指针UserInfo的默认值为:%v\n"
, g)
fmt.Printf(
"int数组的默认值为:%v\n"
, iArray)
fmt.Printf(
"int指针的默认值为:%p\n"
, ip)
fmt.Printf(
"byte指针的默认值为:%p\n"
, bp)
fmt.Printf(
"string指针的默认值为:%p\n"
, fp)
fmt.Printf(
"float64指针的默认值为:%p\n"
, sp)
fmt.Printf(
"byte指针的默认值为:%p\n"
, ssp)
if
ip !=
nil
{
fmt.Printf(
"string指针的默认值为:%d\n"
, *ip)
}
}
运行结果截图:
由上可以知道两个点:
1.各种数据类型怎么输出,对应的d%,v%,s%是什么。(大家可以看一下,后面自己本地测试输出日志也方便)
2.了解各种数据的默认值,总结来说就是:数据类型是0,字符是空字符“”,结构体指针是nil,基础数据结构指针是0x0。
值得注意的是:虽然基础数据类型指针的输出和结构体指针的输出不太一样,但是实际判空的时候,都是视为nil的。例如:
1
2
3
4
var ip
*
int
if
ip!=
nil
{
//不会进入该逻辑,即:ip指向了0x0的时候,是视为nil的
fmt.Printf(
"string指针的默认值为:%d\n"
, *ip)
}
好了,那么了解了各个数据类型的默认值,判空就好做多了。
判断是否初始化(判空)
方法1:
直接判断它和默认值是否一样,是的话就认为是没有初始化的。(这部分主要是了解原理,实际我们开发过程用方法2好点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package
main
import
(
"fmt"
"reflect"
)
type
UserInfo
struct
{
Name
string
Age
int
Sex
string
Flag
bool
}
func
main() {
fmt.
Println
(
"-----------判断类型函数实验----------"
)
var
a
int
var
b
bool
var
c
float64
var
d
byte
var
e
string
var
f UserInfo
var
g *UserInfo
var
ip *
int
var
sp *
string
if
g ==
nil
{
fmt.
Println
(
"nil判断成功"
)
}
var
iSlice []
int
var
iArray [
2
]
int
CheckType(a)
CheckType(b)
CheckType(c)
CheckType(d)
CheckType(e)
CheckType(f)
CheckType(g)
CheckType(ip)
CheckType(sp)
CheckType(iArray)
CheckType(iSlice)
}
// 自己写了一个判空函数,你可以直接看判空部分的逻辑就好了。
func
CheckType(args ...
interface
{}) {
for
_, arg :=
range
args {
fmt.Printf(
"数据类型为:%s\n"
, reflect.TypeOf(arg).Kind().
String
())
//先利用反射获取数据类型,再进入不同类型的判空逻辑
switch
reflect.TypeOf(arg).Kind().
String
() {
case
"int"
:
if
arg ==
0
{
fmt.
Println
(
"数据为int,是空值"
)
}
case
"string"
:
if
arg ==
""
{
fmt.
Println
(
"数据为string,为空值"
)
}
else
{
fmt.
Println
(
"数据为string,数值为:"
, arg)
}
case
"int64"
:
if
arg ==
0
{
fmt.
Println
(
"数据为int64,为空值"
)
}
case
"uint8"
:
if
arg ==
false
{
fmt.
Println
(
"数据为bool,为false"
)
}
case
"float64"
:
if
arg ==
0.0
{
fmt.
Println
(
"数据为float,为空值"
)
}
case
"byte"
:
if
arg ==
0
{
fmt.
Println
(
"数据为byte,为0"
)
}
case
"ptr"
:
if
arg ==
nil
{
//接口状态下,它不认为自己是nil,所以要用反射判空
fmt.
Println
(
"数据为指针,为nil"
)
}
else
{
fmt.
Println
(
"数据不为空,为"
, arg)
}
//反射判空逻辑
if
reflect.ValueOf(arg).IsNil() {
//利用反射直接判空
fmt.
Println
(
"反射判断:数据为指针,为nil"
)
fmt.
Println
(
"nil:"
, reflect.ValueOf(
nil
).IsValid())
//利用反射判断是否是有效值
}
case
"struct"
:
if
arg ==
nil
{
fmt.
Println
(
"数据为struct,为空值"
)
}
else
{
fmt.
Println
(
"数据为struct,默认有数,无法判空,只能判断对应指针有没有初始化,直接结构体无法判断"
)
}
case
"slice"
:
s := reflect.ValueOf(arg)
if
s.
Len
() ==
0
{
fmt.
Println
(
"数据为数组/切片,为空值"
)
}
case
"array"
:
s := reflect.ValueOf(arg)
if
s.
Len
() ==
0
{
fmt.
Println
(
"数据为数组/切片,为空值"
)
}
else
{
fmt.
Println
(
"数据为数组/切片,为"
, s.
Len
())
}
default
:
fmt.
Println
(
"奇怪的数据类型"
)
}
}
}
运行结果截图:
由上可知。基本还是那句话:数据类型默认0,指针类型默认nil(接口类型下,空指针==nil会不通过,要用反射判空),字符类型为空字符串“”。
方式2:
利用反射包的内置函数判空. 正如上面展示的指针判空逻辑。实际上go已经有一个反射包里面封装了判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package
main
import
(
"fmt"
"reflect"
)
type
UserInfo
struct
{
Name
string
Age
int
Sex
string
Flag
bool
}
func
main() {
fmt.
Println
(
"-----------指针类型判空实验----------"
)
var
g *UserInfo
var
ip *
int
var
sp *
string
var
iSlice []
int
CheckTypeByReflectNil(g)
CheckTypeByReflectNil(ip)
CheckTypeByReflectNil(sp)
CheckTypeByReflectNil(iSlice)
fmt.
Println
(
"-----------基础类型判空实验----------"
)
var
a
int
var
b
bool
var
c
float64
var
d
byte
var
e
string
var
f UserInfo
CheckTypeByReflectZero(a)
CheckTypeByReflectZero(b)
CheckTypeByReflectZero(c)
CheckTypeByReflectZero(d)
CheckTypeByReflectZero(e)
CheckTypeByReflectZero(f)
}
func
CheckTypeByReflectNil(arg
interface
{}) {
if
reflect.ValueOf(arg).IsNil() {
//利用反射直接判空,指针用isNil
// 函数解释:isNil() bool 判断值是否为 nil
// 如果值类型不是通道(channel)、函数、接口、map、指针或 切片时发生 panic,类似于语言层的v== nil操作
fmt.Printf(
"反射判断:数据类型为%s,数据值为:%v,nil:%v \n"
,
reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
}
}
func
CheckTypeByReflectZero(arg
interface
{}) {
if
reflect.ValueOf(arg).IsZero() {
//利用反射直接判空,基础数据类型用isZero
fmt.Printf(
"反射判断:数据类型为%s,数据值为:%v,nil:%v \n"
,
reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
}
}
运行结果截图: