持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
go语言的基本数据类型很丰富,光是整数类型就有int8
,int16
,int32
,int64
,uint64
,这些类型在工作中非常常见,go的interface{}
可以接收任意值,所以有些时候在进行类型之间转换,也会变得非常的麻烦,比如,将一个interface{}
转换成int类型,因为我们不知道interface{}
中具体是什么类型,我们只能通过下面的方式进行类型转换
func Convert2Int(v interface{}) (int,error) {
switch s := v.(type) {
case int64:
return int(s), nil
case int32:
return int(s), nil
case int16:
return int(s), nil
case int8:
return int(s), nil
case uint:
return int(s), nil
case uint64:
return int(s), nil
case uint32:
return int(s), nil
case uint16:
return int(s), nil
case uint8:
return int(s), nil
case float64:
return int(s), nil
case float32:
return int(s), nil
default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int", v, v)
}
}
因为他可能是任意类型,所以我们只能一个类型一个类型的去判断,这使得我们的业务代码变得非常的臃肿。而且写这么多的case很有可能一不留神就少些一个,为我们的程序埋下bug
下面来看看开源库cast
是怎么解决这个头疼的问题的
Go Cast
安装cast
go get github.com/spf13/cast
cast
提供了interface{}
类型向任意基本类型的转换API,
我们只需要调用他的api,一行代码就可以搞定interface向任意类型转换
实现原理
来看看他是怎么实现的吧,以ToInt
为例
func ToInt(i interface{}) int {
v, _ := ToIntE(i)
return v
}
func ToIntE(i interface{}) (int, error) {
// 如果是指针类型,那么取出具体对应的值
i = indirect(i)
intv, ok := toInt(i)
if ok {
return intv, nil
}
switch s := i.(type) {
case int64:
return int(s), nil
case int32:
return int(s), nil
case int16:
return int(s), nil
case int8:
return int(s), nil
case uint:
return int(s), nil
case uint64:
return int(s), nil
case uint32:
return int(s), nil
case uint16:
return int(s), nil
case uint8:
return int(s), nil
case float64:
return int(s), nil
case float32:
return int(s), nil
case string:
v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
if err == nil {
return int(v), nil
}
return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
case json.Number:
return ToIntE(string(s))
case bool:
if s {
return 1, nil
}
return 0, nil
case nil:
return 0, nil
default:
return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
}
}
可以看出cast也是通过一个一个case
进行类型转换的
但是他的case要比我们写的要全面的多,
比如bool
类型,会很贴心的转换成0
和1
,
string类型会帮助我们自动通过strconv
转换成基本数据类型。
Cast进阶
cast除了帮我们做了基本数据类型的转换,还帮助我们做了日期相关的转换
interface{}转Duration
func ToDuration(i interface{}) time.Duration {
v, _ := ToDurationE(i)
return v
}
func ToDurationE(i interface{}) (d time.Duration, err error) {
i = indirect(i)
switch s := i.(type) {
case time.Duration:
return s, nil
case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
d = time.Duration(ToInt64(s))
return
case float32, float64:
d = time.Duration(ToFloat64(s))
return
case string:
if strings.ContainsAny(s, "nsuµmh") {
d, err = time.ParseDuration(s)
} else {
d, err = time.ParseDuration(s + "ns")
}
return
default:
err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
return
}
}
根据传入的类型进行不同的处理:
-
如果是
time.Duration
类型,直接返回; -
如果是整型或浮点型,将其数值强制转换为
time.Duration
类型,单位默认为ns
; -
如果是字符串,分为两种情况:如果字符串中有时间单位符号
nsuµmh
,直接调用time.ParseDuration
解析;否则在字符串后拼接ns
再调用time.ParseDuration
解析; -
其他类型解析失败。
时间类型转换
func ToTimeE(i interface{}) (tim time.Time, err error) {
i = indirect(i)
switch v := i.(type) {
case time.Time:
return v, nil
case string:
return StringToDate(v)
case int:
return time.Unix(int64(v), 0), nil
case int64:
return time.Unix(v, 0), nil
case int32:
return time.Unix(int64(v), 0), nil
case uint:
return time.Unix(int64(v), 0), nil
case uint64:
return time.Unix(int64(v), 0), nil
case uint32:
return time.Unix(int64(v), 0), nil
default:
return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
}
}
根据传入的类型执行不同的处理:
-
如果是
time.Time
,直接返回; -
如果是整型,将参数作为时间戳(自 UTC 时间
1970.01.01 00:00:00
到现在的秒数)调用time.Unix
生成时间。Unix
接受两个参数,第一个参数指定秒,第二个参数指定纳秒; -
如果是字符串,调用
StringToDate
函数依次尝试以下面这些时间格式调用time.Parse
解析该字符串。如果某个格式解析成功,则返回获得的time.Time
。否则解析失败,返回错误; -
其他任何类型都无法转换为
time.Time
。