✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
文章目录
Go语言中Type的详细用法教程
在Go语言中,type
关键字是构建自定义数据类型和声明新类型的基础。通过type
,我们可以定义类型别名、结构体、接口、函数类型等多种数据类型,这些自定义类型极大地增强了Go语言的灵活性和表达能力。本文将结合实际案例,详细探讨type
在Go语言中的多种用法。
1. type使用语法
1、定义新类型
type NewTypeName OldTypeName
其中,NewTypeName是新创建的类型别名,OldTypeName是已存在的类型。
2、给存在的类型起别名
type xxx = 类型 ,将某个类型赋值给 xxx,相当于这个类型的别名。程序原酸的时候还是按原类型来处理
type关键字的理解:
1、type 定义一个类型
- type User struct 定义结构体类型
- type User interface 定义接口类型
- type Diy (int、string、…) 自定义类型,全新的类型
2、type 起别名
- type xxx = 类型 ,将某个类型赋值给 xxx,相当于这个类型的别名
- 别名只能在写代码的时候使用,增加代码的可阅读性。
- 真实在项目的编译过程中,它还是原来的类型。
总结:
- type xxx TTT 自定义类型
- type xxx = TTT 起别名
代码示例:
package main
import "fmt"
// var 变量 type 类型(结构体、接口、别名...)
// type的别名用法,全局变量中
// 这是定义了一个新类型 MyInt,是int转换过来的,和int一样,但是不能通int发生操作,类型不同
// 这里他俩MyInt int 是两个类型
// 创建了一种新类型!
type MyInt int
func main() {
var a MyInt = 20 // MyInt
var b int = 10 // int
//自定义的类型和原类型也不能做运算
// invalid operation: a + b (mismatched types MyInt and int)
//fmt.Println(a + b)
//可以进行强制类型转换
// 类型转换: T(v)
fmt.Println(int(a) + b) // 30
//查看他俩数据类型
fmt.Printf("%T\n", a) // main.MyInt
fmt.Printf("%T\n", b) // int
// 给int起一个小名,但是它还得是int type any
type diyint = int //用等号赋值,这里diyint和int是一样的
var c diyint = 30
//查看数据类型,可以看到还是原来的数据类型
fmt.Printf("%T\n", c) // int
//此时的c和基本int类型的数据可以直接进行运算
fmt.Println(c + b) //40
}
/*
type关键字的理解:
1、type 定义一个类型
- type User struct 定义结构体类型
- type User interface 定义接口类型
- type Diy (int、string、....) 自定义类型,全新的类型
2、type 起别名
- type xxx = 类型 ,将某个类型赋值给 xxx,相当于这个类型的别名
- 别名只能在写代码的时候使用,增加代码的可阅读性。
- 真实在项目的编译过程中,它还是原来的类型。
*/
进阶用法
类型别名不仅限于基础类型,也可以用于复合类型,如结构体、切片、映射等。然而,重要的是要理解,类型别名和原始类型在类型系统中是被视为等价的。
示例:类型别名与结构体
type Point struct {
X, Y int
}
type Coord Point
func main() {
var p Point = Point{1, 2}
var c Coord = Coord{3, 4}
// Point 和 Coord 是等价的
var q Point = c // 正确,因为 Point 和 Coord 是等价的
fmt.Println(q) // 输出: {3 4}
}
尽管Point
和Coord
在代码中是两个不同的类型名称,但在Go的类型系统中,它们被视为等价。因此,Point
类型的变量可以赋值为Coord
类型的值,反之亦然。
2. 结构体类型(Struct Types)
定义与基本用法
结构体是Go语言中的一种复合数据类型,用于将多个不同类型的变量组合成一个单一的类型。结构体类型允许我们创建具有多个属性的自定义类型。
语法
type StructName struct {
Field1 FieldType1
Field2 FieldType2
// ...
}
示例
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // 输出: {Alice 30}
}
在上述代码中,我们定义了一个Person
结构体,它有两个字段:Name
和Age
。然后,我们创建了一个Person
类型的变量p
,并初始化了它的字段。
结构体字段的访问与修改
结构体字段可以通过点操作符.
来访问和修改。
p.Name = "Bob"
fmt.Println(p.Name) // 输出: Bob
结构体标签(Struct Tags)
结构体字段还可以包含标签(也称为元数据),这些标签用于为字段提供额外的信息,常用于JSON序列化/反序列化等场景。
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
匿名结构体与内嵌结构体
Go还允许定义匿名结构体和将结构体作为另一个结构体的字段(内嵌结构体)。
匿名结构体
var person = struct {
Name string
Age int
}{"Alice", 30}
fmt.Println(person) // 输出: {Alice 30}
内嵌结构体
type Address struct {
City, State string
}
type Person struct {
Name string
Age int
Address
}
func main() {
p := Person{
Name: "Alice",
Age: 30,
Address: Address{
City: "New York",
State: "NY",
},
}
fmt.Println(p) // 输出: {Alice 30 {New York NY}}
fmt.Println(p.City) // 输出: New York
}
在上述代码中,Address
结构体被内嵌到Person
结构体中。这允许我们直接通过Person
类型的实例访问Address
结构体的字段。
3. 接口类型(Interface Types)
定义与基本用法
接口是一种抽象类型,它定义了一组方法,但不实现它们。接口由一组方法签名组成,这些方法的具体实现由其他类型(如结构体)提供。
语法
type InterfaceName interface {
Method1(param1 ParamType1) ReturnType1
Method2(param1 ParamType2, param2 ParamType2) ReturnType2
// ...
}
示例
package main
import "fmt"
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
var s Shape = Circle{Radius: 5}
fmt.Println(s.Area()) // 输出: 78.53981633974483
}
在上述代码中,我们定义了一个Shape
接口和一个Circle
结构体。Circle
结构体实现了Shape
接口中的Area
方法。然后,我们将Circle
的实例赋值给Shape
接口类型的变量s
,并调用了Area
方法。
接口与多态
接口是实现多态性的关键。通过接口,我们可以编写不依赖于具体实现的代码,从而使代码更加灵活和可重用。
示例:使用接口实现多态
package main
import "fmt"
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func printArea(s Shape) {
fmt.Println(s.Area())
}
func main() {
circle := Circle{Radius: 5}
rectangle := Rectangle{Width: 4, Height: 6}
printArea(circle) // 输出: 78.53981633974483
printArea(rectangle) // 输出: 24
}
在上述代码中,我们定义了一个printArea
函数,它接受一个Shape
接口类型的参数。这意味着它可以接受任何实现了Shape
接口的类型的实例作为参数。这样,我们就实现了多态性。
4. 函数类型(Function Types)
定义与基本用法
在Go中,我们还可以定义函数类型,即函数的签名。函数类型允许我们将函数作为参数传递给其他函数,或者将函数作为返回值。
语法
type FunctionName func(param1 ParamType1, param2 ParamType2) ReturnType
示例
package main
import "fmt"
type AddFunc func(a, b int) int
func add(a, b int) int {
return a + b
}
func apply(f AddFunc, x, y int) int {
return f(x, y)
}
func main() {
result := apply(add, 5, 3)
fmt.Println(result) // 输出: 8
}
在上述代码中,我们定义了一个AddFunc
函数类型,它接受两个int
类型的参数并返回一个int
类型的结果。然后,我们定义了一个add
函数,它符合AddFunc
的签名。最后,我们定义了一个apply
函数,它接受一个AddFunc
类型的参数和两个int
类型的参数,并返回调用该函数的结果。
在Go语言中,type
关键字不仅用于定义基础的数据类型别名、结构体、接口和函数类型,还有一些高级用法和特性。接下来,我们将继续探讨 type
在Go语言中的其他用法。
5. 指针类型(Pointer Types)
虽然指针类型本身不是通过 type
关键字直接定义的(因为所有类型都可以有指针),但理解指针类型对于深入掌握Go语言至关重要。在Go中,指针类型允许我们直接访问变量的内存地址,并通过指针来修改变量的值。
示例
package main
import "fmt"
func main() {
x := 10
p := &x // p 是一个指向 x 的指针
fmt.Println(*p) // 输出: 10
*p = 20 // 通过指针修改 x 的值
fmt.Println(x) // 输出: 20
}
在上面的代码中,&x
获取了变量 x
的内存地址,并将其赋值给指针变量 p
。*p
是对指针 p
进行解引用,即获取指针所指向的值。
6. 类型断言(Type Assertions)
类型断言提供了一种访问接口值底层具体值的方式。类型断言主要用于将接口类型的变量转换为具体的类型。
语法
value, ok := x.(T)
如果 x
不是 T
类型,则 ok
会是 false
,且 value
会是 T
类型的零值。如果 x
是 T
类型,则 ok
会是 true
,且 value
会是 x
的值。
示例
package main
import "fmt"
func main() {
var i interface{} = "hello"
s, ok := i.(string)
if ok {
fmt.Println(s) // 输出: hello
} else {
fmt.Println("类型断言失败")
}
// 尝试将i断言为int类型,会失败
n, ok := i.(int)
if !ok {
fmt.Println("类型断言失败")
}
}
7. 类型选择(Type Switches)
类型选择是 switch
语句的一个变种,用于在多个类型之间进行选择。它通常与接口一起使用,以检查接口值持有的具体类型。
语法
switch v := x.(type) {
case T1:
// 处理 T1 类型的 v
case T2:
// 处理 T2 类型的 v
// ...
default:
// 处理所有其他类型
}
示例
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("整型: %v\n", v)
case string:
fmt.Printf("字符串型: %q\n", v)
default:
fmt.Printf("未知类型\n")
}
}
func main() {
do(21)
do("hello")
do(true)
}
8. 自定义类型的方法
在Go中,你可以为任何自定义类型(结构体、类型别名等)定义方法。方法是附加到类型上的函数,其第一个参数是接收者(receiver),接收者指定了方法所属的类型。
示例
package main
import "fmt"
type MyInt int
// 为 MyInt 类型定义方法
func (m MyInt) SayHello() {
fmt.Println("Hello from MyInt:", m)
}
func main() {
var x MyInt = 10
x.SayHello() // 输出: Hello from MyInt: 10
}
9. 类型系统的高级特性
Go的类型系统还包含一些高级特性,如空接口(interface{}
)、隐式接口、类型嵌入(在结构体中使用)和接口的组合等。这些特性为Go提供了强大的灵活性和表达力。
空接口
空接口 interface{}
没有定义任何方法,因此任何类型都实现了空接口。空接口通常用于需要存储任意类型值的场景,如标准库中的 fmt.Println
函数和 json.Marshal
函数。
隐式接口
Go的接口是隐式的,意味着我们不需要显式声明一个类型实现了某个接口。只要类型实现了接口中定义的所有方法,那么它就自动实现了该接口。
类型的零值
Go中的每个类型都有一个零值。对于数值类型,零值是0;对于布尔类型,零值是false
;对于字符串,零值是空字符串""
;对于指针、切片、映射、通道(channel)、函数和接口,零值是nil
。了解类型的零值对于编写健壮的Go代码至关重要。
10. 总结
Go语言中的 type
关键字是构建自定义数据类型和声明新类型的基础。通过类型别名、结构体、接口、函数类型等,Go提供了丰富的类型系统,使得我们可以编写出既灵活又强大的代码。掌握 type
的用法,是深入理解Go语言的关键。