2.5 类型
- 可以使用【type 类型名字 底层类型】来定义自己的数据类型
- 自定义的两种类型的不同对象之间,哪怕底层类型一样也不难使用二元运算符(如==, +, *, /等)
- 底层数据类型一样的话,可以进行类型转换。如同为数值类型的float和int可以相互转换,而string和float不行。
- 可以为自定义类型定义方法
package main
import "fmt"
// 1. 自定义类型
type Celsius float64 // 摄氏温度
type Fahrenheit float64 // 华氏温度
type Second int // 秒
type Path string // 路径
// 使用自己自定义的类型来声明变量
const (
AbsoluteZeroC Celsius = -273.15 // 绝对零度
FreezingC Celsius = 0 // 结冰点温度
BoilingC Celsius = 100 // 沸水温度
OneMinute Second = 60
Root Path = "C:"
)
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } // 摄氏度转为华氏度
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) } // 华氏度转为摄氏度
// 4.Celsius类型的方法
func (c Celsius) String() string {return fmt.Sprintf("%g°C", c)}
func main() {
// 摄氏度转为华氏度
AbsoluteZeroF := CToF(AbsoluteZeroC)
FreezingF := CToF(FreezingC)
BoilingF := CToF(BoilingC)
fmt.Println(AbsoluteZeroC, AbsoluteZeroF)
fmt.Println(FreezingC, FreezingF)
fmt.Println(BoilingC, BoilingF)
//2.自定义的两种类型的不同对象之间,哪怕底层类型一样也不难使用二元运算符
//test := AbsoluteZeroF + AbsoluteZeroC // 会报错,虽然底层类型一样为float32,但是自定义的类型不一样
// 同样 ==, +, *, /等二元运算符不能使用
// 3. 底层数据类型一样的话,可以进行类型转换。
test1 := Fahrenheit(AbsoluteZeroC) // 底层类型一样,所以可以转换
test2 := Fahrenheit(OneMinute) // 底层类型一样,都是数值类型,可以转换
//test3 := Fahrenheit(Root) // 不行,底层类型一个是数值类型,一个是字符串类型
fmt.Println(test1)
fmt.Println(test2)
fmt.Println(AbsoluteZeroC.String())
}
2.6 包和文件
- 一个包可以有多个go文件,在同一个包内所有的go文件都要声明该包的名字
- 包中的变量函数要给外部使用的话,名字的首字母一定要大写。首字母为小写表示是该包的私有化变量
- 包中的一级变量在包中的所有文件时共享的
- 导入包函数时候可以重新命名。可能有两个包的名字是一样的,但是实现的功能不一样,并且我们需要同时使用这两个包,我们可以在导入的时候给包重新命名。
- 每个包内的文件都可以定义一个init()初始化的函数,这个函数会在包被调用的时候进行初始化的操作(编译器会自动识别并运行这个函数)。初始化工作是自下而上进行的,main包最后被初始化。比如main包调用了A包,A包调用了C包,那么C包中init()函数最先被执行,随后是B,再到main。
2.7 作用域
全局变量的作用域最大,其次是局部变量的作用域。前者作用域是包级,后者是函数局
对于多个同名的声明编译器会做一定的处理:首先从最内层的词法域向全局的作用域查找。如果查找失败,则报告“未声明的名字”这样的错误。如果该名字在内部和外部的块分别声明过,则内部块的声明首先被找到。在这种情况下,内部声明屏蔽了外部同名的声明,让外部的声明的名字无法被访问。
func f() {}
var g = "g"
func main() {
f := "f"
fmt.Println(f) // "f"; 局部变量f把包级f()覆盖了
fmt.Println(g) // "g"; 包级变量
fmt.Println(h) // compile error: undefined: h
}
《Go语言圣经》举了一个很有趣的例子。
var cwd string
func init() {
cwd, err := os.Getwd() // NOTE: wrong!
if err != nil {
log.Fatalf("os.Getwd failed: %v", err)
}
log.Printf("Working directory = %s", cwd)
}
上面那段代码中init()函数执行后,全局变量的值并没有被正确的赋值。这是因为编译器在init中重新定义了一个局部变量cwd两个cwd是不一样的。这样的错误很隐蔽,很容易导致Bug。
正确做法是单独声明err
var cwd string
func init() {
var err error
cwd, err = os.Getwd()
if err != nil {
log.Fatalf("os.Getwd failed: %v", err)
}
}
主要参考文献:《Go语言圣经》
撩我?
我的公众号:Kyda