类型转换
go语言提供了一种在不同但是相互兼容的类型之间相互转换的方式,并且这种转换非常安全。非数值类型之间的转换不会丢失精读。 但是对于数值类型之间的转换,可能会发生丢失精度或其他问题。
转换语法
resultOfType := Type(exportession)
例如
x := uint16(6500)
y := int16(x)
由于x超出了int16的范围,所以y的值会被设置成-535。
对于数字,本质上将我们可以将任意的整型或者浮点型数据转换成别的整型或浮点型(要考虑丢失精度问题)。
string类型转换
一个字符串可以转换成一个[]byte(其底层位UTF-8的字节)或者一个[]rune(Unicode码点),并且[]byte和[]reune都可以转换成一个字符串类型。单个字符是一个rune类型数据(即int32), 可以转换成一个单字符的字符串。
类型转换的高级用法
Go语言的interface{}类型用于表示空接口,同时也可以用来表示任意Go类型的值。 此外,我们可以使用类型断言,类型开关或者GO语言的reflect包的类型检查将一个interface{}类型的值转换成一个实际数据的值。
1、类型断言
语法
resultOfType, boolean := expression.(Type) //安全类型断言 这里的Type是一个具体的Go类型,如int
resultOfType := expression.(Type) //非安全类型断言,失败是panic
成功的安全类型断言将返回目标类型的值和表示成功的true。如果安全类型断言失败(即表达式的类型与声明的类型不兼容),将返回目标类型的零值和false。
非安全类型断言要么返回一个目标类型的值,要么panic抛出异常。
示例
var i interface{} = 99
var s interface{} = []string{"left", "right"}
j := i.(int) //j是int类型的数据(或者panic)
fmt.Printf("%T -> %d \n", j, j)
if i, ok := i.(int); ok{
fmt.Prinf("%T -> %d \n", i, j) //i 是一个int类型的影子变量
}
if s, ok := s.([]string); ok{
fmt.Prinf("%T -> %q \n", s, s) //s 是一个[]string类型的影子变量
}
输出
int -> 99
int -> 99
[]string -> ["left" "right"]
做类型断言时将结果复制给与原始变量同名的变量是很常见的事情,即使用影子变量。
上面示例中,如果我们输出原始的i和s变量(两者都是interface{}类型),它们可以以int和[]string类型的形式输出。因为fmt包的打印函数遇到interface{}类型时,会智能的打印实际类型的值。
2、类型开关
当我们使用interfa{}类型的变量时,我们常常需要访问其底层值。如果知道其底层值类型,就可以使用类型断言,但是如果其类型可能是很多类型的一种,就可以使用类型开关。
语法
switch optional; typeSwitch{
case typeList:
...
case typeList:
default:
示例
package main
import "fmt"
func main() {
classfier(10, "string", 1.2)
}
func classfier(items ...interface{}) {
for i, x := range items {
switch x.(type) {
case bool:
fmt.Printf("param #%d is a bool\n", i)
case float64:
fmt.Printf("param #%d is a float64\n", i)
case int, int8, int16, int32, int64:
fmt.Printf("param #%d is a int\n", i)
case string:
fmt.Printf("param #%d is a string\n", i)
case nil:
fmt.Printf("param #%d is a nil\n", i)
default:
fmt.Printf("param #%d is a unknow\n", i)
}
}
}
输出
param #0 is a int
param #1 is a string
param #2 is a float64
这里使用的类型开关守护与类型断言里的格式一样,即 var.(Type),但是这里的type是一个关键字而非实际类型,用于表示任意类型。
类型开关的升级用法:
当我们解析JSON格式的数据,我们必须将数据转换成相对应的Go语言数据类型。这也可以通过GO标准库的json.Unmarshal()函数来实现。 我们想该函数传入一个只想结构体的指针,该结构体又与该json数据字段相匹配,那么该函数就会将JSON数据中对应的数据项填充到结构体的每一个字段。 但是如果我们事先不知道JSON数据的结构,那么就不能给json.Unmarshal()函数传入一个结构体。这种情况下,我们可以给该函数传入一个指向interfa{}的指针,这样json.Unmarshal()函数就会将其设置成引用一个map[string]interface{}类型值,其键位json字段的名字,值位对应的保存为interfa{}的值。
示例
func JsonUnkow() {
MA := []byte(`{"name": "Massachusetts", "area": 27336, "water": 25.7, "senators":["John Kerry", "Scott Brown"]}`)
var object interface{}
if err := json.Unmarshal(MA, &object); err != nil {
fmt.Println(err)
} else {
jsonObject := object.(map[string]interface{}) //将object类型转换成 map[string]interface{}
fmt.Println(jsonObjectAsString(jsonObject))
}
}
//将map中的interface{}类型值转换为实际类型的值
func jsonObjectAsString(jsonObject map[string]interface{}) string {
var buffer bytes.Buffer
buffer.WriteString("{")
comma := ""
for key, value := range jsonObject {
buffer.WriteString(comma)
switch value := value.(type) { //影子变量
case nil:
fmt.Fprintf(&buffer, "%q: null", key)
case bool:
fmt.Fprintf(&buffer, "%q: %t", key, value)
case float64:
fmt.Fprintf(&buffer, "%q: %f", key, value)
case string:
fmt.Fprintf(&buffer, "%q: %q", key, value)
case []interface{}:
fmt.Fprintf(&buffer, "%q: [", key)
innerComma := ""
for _, s := range value {
if s, ok := s.(string); ok {
fmt.Fprintf(&buffer, "%s%q", innerComma, s)
innerComma = ", "
}
}
buffer.WriteString("]")
}
comma = ", "
}
buffer.WriteString("}")
return buffer.String()
}
上面的例子给出了如何反序列化一个其内部结构未知的原始json对象,如何创建和打印json对象的字符串表示。
输出打印
"{name": "Massachusetts", "area": 27336.000000, "water": 25.700000, "senators": ["John Kerry", "Scott Brown"]}
3、反射
反射的详细使用参考GO语言反射规则。
4、日志
golang日志标准库只支持一些基本的日志操作,需要更丰富的日志操作,这里我们使用github.com/golang/glog这个第三方库。
glog只提供了少数几个选项,通过命令行控制,例如:
-log_dir: 日志文件保存目录
-alsologtostderr: 日志写入文件的同时,输出到stderr
-v:配置V输出的等级。
package main
import (
"flag"
"github.com/golang/glog"
)
func main() {
//初始化glog命令行参数
flag.Parse() // 1
glog.Info("This is a Info log") // 2
glog.Warning("This is a Warning log")
glog.Error("This is a Error log")
glog.V(1).Infoln("level 1") // 3
glog.V(2).Infoln("level 2")
glog.Flush() // 4
}
有几点说明:
1、高等级的日志会同时输出到比它等级低的文件中,例如error日志会同时输出到error文件,warning文件,info文件中,依次类推;
2、启动时,调用flag.Parse用来初始化glog的参数,例如xxx.exe -log_dir=”./”
3、程序退出时,需要调用glog.Flush(),将日志写入文件中。
4、V函数。默认的v选项为0,可以通过命令行设置v值。V函数的功能是,当V函数的参数高于glog的v值时,不会执行后续的Info函数,否则就执行。
参考文档:
GO语言程序设计(作者:Mark Summerfield 译者: 许式伟、吕桂华、徐立、何李石)5.2章节。