Go 语言中的类型
在 Go 语言中,每个值都属于一个类型。类型用于定义该值的内存布局和操作集。Go 语言中的类型包括基本类型和复合类型。
基本类型
Go 语言中的基本类型包括布尔类型、数字类型、字符类型和字符串类型。
-
布尔类型只有两个取值 true 和 false
-
数字类型包括整数类型和浮点数类型
-
字符类型用单引号括起来的单个 Unicode 字符,字符串类型用双引号括起来的 Unicode 字符串。
示例代码:
package main
import "fmt"
func main() {
// 布尔类型
var b bool = true
fmt.Println(b) // 输出 true
// 数字类型
var i int = 123
var f float64 = 3.14
var c complex128 = 1 + 2i
fmt.Println(i, f, c) // 输出 123 3.14 (1+2i)
// 字符类型
var ch rune = '中'
fmt.Println(ch) // 输出 20013
// 字符串类型
var str string = "Hello, 世界"
fmt.Println(str) // 输出 Hello, 世界
}
复合类型
Go 语言中的复合类型包括数组、切片、映射、结构体和指针。
-
数组是一种固定长度的数据结构,它由一系列相同类型的元素组成,这些元素在内存中是连续存储的。数组的长度是在定义数组时指定的,一旦定义了数组的长度,就不能再改变。
-
切片是一种动态数组,它是一个引用类型,可以动态地增加或减少元素的数量。切片本身并不存储任何数据,它只是对底层数组的一个引用,相当于底层数组的一个视图。
-
映射(map)是一种无序的键值对集合,其中每个键都是唯一的。映射的键和值可以是任意类型,只要它们支持相应的操作。映射是一种引用类型,可以用 make 函数创建。
-
结构体是一种复合数据类型,它由一系列具有相同或不同类型的成员组成。每个成员可以是任何类型,包括基本类型、数组、切片、结构体、函数等等。
-
指针是一种变量,它存储了另一个变量的内存地址。
示例代码:
package main
import "fmt"
func main() {
// 数组类型
var arr [3]int = [3]int{1, 2, 3}
fmt.Println(arr) // 输出 [1 2 3]
// 切片类型
var slice []int = []int{1, 2, 3}
slice = append(slice, 4)
fmt.Println(slice) // 输出 [1 2 3 4]
// 映射类型
var m map[string]int = map[string]int{"apple": 1, "banana": 2}
m["orange"] = 3
fmt.Println(m) // 输出 map[apple:1 banana:2 orange:3]
// 结构体类型
type Fruit struct {
Name string
Price float64
}
var apple Fruit = Fruit{Name: "apple", Price: 3.0}
fmt.Println(apple) // 输出 {apple 3}
// 指针类型
var p *int = &arr[0]
fmt.Println(*p) // 输出 1
}
Go 语言中的接口
在 Go 语言中,接口是一种类型,它定义了一组方法的签名。
接口类型没有实现,只有实现了接口的具体类型才能被赋值给接口类型变量。
一个类型只要实现了接口类型的所有方法,就被认为是该接口类型的实现类型。
接口可以被用来定义一种通用的类型,不同的具体类型可以实现同一个接口,从而实现多态性。
示例代码:
package main
import "fmt"
// 定义水果接口类型
type Fruit interface {
GetName() string
GetPrice() float64
}
// 定义苹果类型并实现 Fruit 接口
type Apple struct {
name string
price float64
}
func (apple Apple) GetName() string {
return a.name
}
func (a Apple) GetPrice() float64 {
return a.price
}
// 定义芒果类型并实现 Fruit 接口
type Mango struct {
name string
price float64
}
func (m Mango) GetName() string {
return m.name
}
func (m Mango) GetPrice() float64 {
return m.price
}
// 定义西瓜类型并实现 Fruit 接口
type Watermelon struct {
name string
price float64
}
func (w Watermelon) GetName() string {
return w.name
}
func (w Watermelon) GetPrice() float64 {
return w.price
}
// 使用接口类型变量
func PrintFruit(f Fruit) {
fmt.Println("Name:", f.GetName(), "Price:", f.GetPrice())
}
func main() {
apple := Apple{name: "apple", price: 3.0}
PrintFruit(apple) // 输出 Name: apple Price: 3
mango := Mango{name: "mango", price: 4.5}
PrintFruit(mango) // 输出 Name: mango Price: 4.5
watermelon := Watermelon{name: "watermelon", price: 2.0}
PrintFruit(watermelon) // 输出 Name: watermelon Price: 2
}
首先定义了一个 Fruit 接口类型,它包含两个方法 GetName() 和 GetPrice()。
然后是三个具体类型 Apple、Mango 和 Watermelon,它们都实现了 Fruit 接口的 GetName() 和 GetPrice() 方法。
最后使用接口类型变量 PrintFruit() 打印出三个具体类型的名称和价格。
需要注意的是,接口类型可以作为参数类型和返回类型,从而实现更为灵活的函数或方法,比如:
func GetFruit(name string) Fruit {
switch name {
case "apple":
return Apple{name: "apple", price: 3.0}
case "mango":
return Mango{name: "mango", price: 4.5}
case "watermelon":
return Watermelon{name: "watermelon", price: 2.0}
default:
return nil
}
}
上述 GetFruit() 函数根据名称返回相应的 Fruit 接口类型,这种方式可以隐藏具体类型的实现细节,提高代码的可维护性和可扩展性。
判断是否实现
除了使用接口类型作为参数类型和返回类型,还可以使用类型断言来判断一个接口类型是否实现了某个接口,比如:
func IsFruit(f Fruit) bool {
_, ok := f.(Fruit)
return ok
}
上述 IsFruit() 函数判断一个接口类型是否实现了 Fruit 接口,如果是则返回 true,否则返回 false。