詳解Go語言各種常見類型的默認(rèn)值和判空方法
起因(解決的問題)
由于在項目中設(shè)計到了類型的判空,所以突然好奇起來,每個類型如果只是聲明,而沒有初始化,那么默認(rèn)值是多少?怎么判斷它是不是空值?所以去整理了一下
基本類型的默認(rèn)值
1.常見的基本數(shù)據(jù)類型有:數(shù)據(jù)類型(int,uint,float之類的),字符串(string),結(jié)構(gòu)體,數(shù)組,指針。
2.那么他們的默認(rèn)值是:
數(shù)據(jù)類型
默認(rèn)值
int
0
float
0.00000
string
“”
結(jié)構(gòu)體
根據(jù)結(jié)構(gòu)體內(nèi)部的基礎(chǔ)數(shù)據(jù)類型進(jìn)行初始化賦值,下面會有demo
數(shù)組(切片)
空數(shù)組
指針
nil
3.例子:
package
main
?
?import
(
????
"fmt"
)
?
?type
UserInfo
struct
{
????
Name
string
????
Age?
int
????
Sex?
string
????
Flag
bool
}
?
?// main函數(shù)
func
main() {
????
PrintDefault()
}
?
?// 輸出默認(rèn)值的函數(shù)
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
(
"-------默認(rèn)值列表--------"
)
????
fmt.Printf(
"int的默認(rèn)值為:%d\n"
, a)
????
fmt.Printf(
"bool的默認(rèn)值為:%t\n"
, b)
????
fmt.Printf(
"float64的默認(rèn)值為:%f\n"
, c)
????
fmt.Printf(
"byte的默認(rèn)值為:%b\n"
, d)
????
fmt.Printf(
"string的默認(rèn)值為:%s\n"
, e)
????
fmt.Printf(
"結(jié)構(gòu)體UserInfo的默認(rèn)值為:%v\n"
, f)
????
fmt.Printf(
"結(jié)構(gòu)體指針UserInfo的默認(rèn)值為:%v\n"
, g)
????
fmt.Printf(
"int數(shù)組的默認(rèn)值為:%v\n"
, iArray)
????
fmt.Printf(
"int指針的默認(rèn)值為:%p\n"
, ip)
????
fmt.Printf(
"byte指針的默認(rèn)值為:%p\n"
, bp)
????
fmt.Printf(
"string指針的默認(rèn)值為:%p\n"
, fp)
????
fmt.Printf(
"float64指針的默認(rèn)值為:%p\n"
, sp)
????
fmt.Printf(
"byte指針的默認(rèn)值為:%p\n"
, ssp)
????
if
ip !=
nil
{
????????
fmt.Printf(
"string指針的默認(rèn)值為:%d\n"
, *ip)
????
}
}
運(yùn)行結(jié)果截圖:
由上可以知道兩個點(diǎn):
1.各種數(shù)據(jù)類型怎么輸出,對應(yīng)的d%,v%,s%是什么。(大家可以看一下,后面自己本地測試輸出日志也方便)
2.了解各種數(shù)據(jù)的默認(rèn)值,總結(jié)來說就是:數(shù)據(jù)類型是0,字符是空字符“”,結(jié)構(gòu)體指針是nil,基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)指針是0x0。
值得注意的是:雖然基礎(chǔ)數(shù)據(jù)類型指針的輸出和結(jié)構(gòu)體指針的輸出不太一樣,但是實際判空的時候,都是視為nil的。例如:
var ip
*
int
if
ip!=
nil
{
//不會進(jìn)入該邏輯,即:ip指向了0x0的時候,是視為nil的
????????
fmt.Printf(
"string指針的默認(rèn)值為:%d\n"
, *ip)
????
}
好了,那么了解了各個數(shù)據(jù)類型的默認(rèn)值,判空就好做多了。
判斷是否初始化(判空)
方法1:
直接判斷它和默認(rèn)值是否一樣,是的話就認(rèn)為是沒有初始化的。(這部分主要是了解原理,實際我們開發(fā)過程用方法2好點(diǎn))
package
main
?
?import
(
????
"fmt"
????
"reflect"
)
?
?type
UserInfo
struct
{
????
Name
string
????
Age?
int
????
Sex?
string
????
Flag
bool
}
?
?func
main() {
????
fmt.
Println
(
"-----------判斷類型函數(shù)實驗----------"
)
????
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)
}
?
?// 自己寫了一個判空函數(shù),你可以直接看判空部分的邏輯就好了。
func
CheckType(args ...
interface
{}) {
????
for
_, arg :=
range
args {
????????
fmt.Printf(
"數(shù)據(jù)類型為:%s\n"
, reflect.TypeOf(arg).Kind().
String
())
//先利用反射獲取數(shù)據(jù)類型,再進(jìn)入不同類型的判空邏輯
????????
switch
reflect.TypeOf(arg).Kind().
String
() {
????????
case
"int"
:
????????????
if
arg ==
0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為int,是空值"
)
????????????
}
????????
case
"string"
:
????????????
if
arg ==
""
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為string,為空值"
)
????????????
}
else
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為string,數(shù)值為:"
, arg)
????????????
}
????????
case
"int64"
:
????????????
if
arg ==
0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為int64,為空值"
)
????????????
}
????????
case
"uint8"
:
????????????
if
arg ==
false
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為bool,為false"
)
????????????
}
????????
case
"float64"
:
????????????
if
arg ==
0.0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為float,為空值"
)
????????????
}
????????
case
"byte"
:
????????????
if
arg ==
0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為byte,為0"
)
????????????
}
????????
case
"ptr"
:
????????????
if
arg ==
nil
{
//接口狀態(tài)下,它不認(rèn)為自己是nil,所以要用反射判空
????????????????
fmt.
Println
(
"數(shù)據(jù)為指針,為nil"
)
????????????
}
else
{
????????????????
fmt.
Println
(
"數(shù)據(jù)不為空,為"
, arg)
????????????
}
????????????
//反射判空邏輯
????????????
if
reflect.ValueOf(arg).IsNil() {
//利用反射直接判空
????????????????
fmt.
Println
(
"反射判斷:數(shù)據(jù)為指針,為nil"
)
????????????????
fmt.
Println
(
"nil:"
, reflect.ValueOf(
nil
).IsValid())
//利用反射判斷是否是有效值
????????????
}
????????
case
"struct"
:
????????????
if
arg ==
nil
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為struct,為空值"
)
????????????
}
else
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為struct,默認(rèn)有數(shù),無法判空,只能判斷對應(yīng)指針有沒有初始化,直接結(jié)構(gòu)體無法判斷"
)
????????????
}
????????
case
"slice"
:
????????????
s := reflect.ValueOf(arg)
????????????
if
s.
Len
() ==
0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為數(shù)組/切片,為空值"
)
????????????
}
????????
case
"array"
:
????????????
s := reflect.ValueOf(arg)
????????????
if
s.
Len
() ==
0
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為數(shù)組/切片,為空值"
)
????????????
}
else
{
????????????????
fmt.
Println
(
"數(shù)據(jù)為數(shù)組/切片,為"
, s.
Len
())
????????????
}
????????
default
:
????????????
fmt.
Println
(
"奇怪的數(shù)據(jù)類型"
)
????????
}
????
}
}
運(yùn)行結(jié)果截圖:
由上可知。基本還是那句話:數(shù)據(jù)類型默認(rèn)0,指針類型默認(rèn)nil(接口類型下,空指針==nil會不通過,要用反射判空),字符類型為空字符串“”。
方式2:
利用反射包的內(nèi)置函數(shù)判空. 正如上面展示的指針判空邏輯。實際上go已經(jīng)有一個反射包里面封裝了判斷
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
(
"-----------基礎(chǔ)類型判空實驗----------"
)
????
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
????????
// 函數(shù)解釋:isNil() bool??? 判斷值是否為 nil
????????
// 如果值類型不是通道(channel)、函數(shù)、接口、map、指針或 切片時發(fā)生 panic,類似于語言層的v== nil操作
????????
fmt.Printf(
"反射判斷:數(shù)據(jù)類型為%s,數(shù)據(jù)值為:%v,nil:%v \n"
,
????????????
reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
????
}
}
?
?func
CheckTypeByReflectZero(arg
interface
{}) {
????
if
reflect.ValueOf(arg).IsZero() {
//利用反射直接判空,基礎(chǔ)數(shù)據(jù)類型用isZero
????????
fmt.Printf(
"反射判斷:數(shù)據(jù)類型為%s,數(shù)據(jù)值為:%v,nil:%v \n"
,
????????????
reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
????
}
}
運(yùn)行結(jié)果截圖: