type
-
Go语言提供了type关键字用于一些特殊情况的自定义。它发挥的作用和C++中typedef的作用十分相似,或者说,你甚至可以把它看成Go语言中的typedef -
对于一些复杂的数据类型,我们可以使用
type使其拥有别名
package main
import (
"fmt"
"reflect"
)
type stringList []string
func main() {
strList := stringList{"test1", "test2"}
fmt.Println("strList的数据类型:", reflect.TypeOf(strList))
fmt.Println("strList的值:", reflect.ValueOf(strList))
}
- 对于一些枚举类型,我们也可以使用别名使得其表达的意思更为清晰
package main
import (
"fmt"
)
type Grade int
const (
FirstGrade Grade = iota
SecondGrade
ThirdGrade
FourthGrade
FifthGrade
SixthGrade
)
func main() {
fmt.Println("FirstGrade :", FirstGrade)
fmt.Println("SecondGrade:", SecondGrade)
fmt.Println("ThirdGrade :", ThirdGrade)
fmt.Println("FourthGrade:", FourthGrade)
fmt.Println("FifthGrade :", FifthGrade)
fmt.Println("SixthGrade :", SixthGrade)
}
- 后文中讲到的自定义结构体类型其实也会用到
type,如此你可以这么看:我们使用type Name struct{ }的形式声明了一个结构体,并把这个结构体别名为Name,是不是一下就理解为何要声明的时候使用到type关键字了呢?
结构体
结构体类型
- 结构体类型的变量的定义大致形式如下所示
var struct_variable_type struct {
member definition
member definition
...
member definition
}
- 这是一个变量,这意味着我们可以在函数内定义并使用它
package main
import (
"fmt"
"reflect"
)
func main() {
var student1 struct {
Name string
Age int
}
student1.Name = "Codey"
student1.Age = 18
fmt.Println("student1数据类型:", reflect.TypeOf(student1))
fmt.Println("student1的值:", reflect.ValueOf(student1))
}
- 如果要使用变量的简易定义形式
:=,需要注意对应值的映射后一定要加,
package main
import (
"fmt"
"reflect"
)
func main() {
student2 := struct {
Name string
Age int
}{
Name: "Codey",
Age: 18, //这个逗号千万不能忘记,若是和大括号同行,这个逗号才可以省略
}
fmt.Println("student2数据类型:", reflect.TypeOf(student2))
fmt.Println("student2的值:", reflect.ValueOf(student2))
}
自定义结构体类型
- 自定义结构体的方式和其它语言差不多,只是需要
type关键字需要额外记忆
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
}
func main() {
var student1 Student
student1.Name = "Codey"
student1.Age = 18
fmt.Println("student1数据类型:", reflect.TypeOf(student1))
fmt.Println("student1的值:", reflect.ValueOf(student1))
}
- 事实上,
Go语言既不是面向对象的,也不是面向过程的,身为一个极其灵活的语言,虽然它不提供面向对象类似语言的特质(拥有类,可以在类内写构造函数等),但是如果我们想要实现类似的对象.函数式的调用方法,该怎么实现?
package main
import (
"fmt"
)
type Student struct {
Name string
Age int
}
func (s Student) PrintAge() {
fmt.Println(s.Age)
}
func main() {
var student Student
student.Name = "Tom"
student.Age = 18
fmt.Println("student年龄:")
student.PrintAge()
}
- 如果无法理解,可以换另一种方式来看:
PrintAge()函数前接受一个s类型的对象,定义一个函数使得传入的对象可以调用这个函数,从而实现模拟面向对象编程的效果
结构体内嵌类型
- 结构体在定义属性字段的时候可以只写类型,不写属性名,由编译器自主推导为相应字段赋值。但一个类型的字段只能写一个,因为编译器无法自然顺延相应类型的推导值。像这样只定义类型,不写属性名的字段被称为匿名字段
package main
import "fmt"
type Student struct {
Name string
Age int
int
}
func newStudent(name string, age int) Student {
return Student{Name: name, Age: age, int: 10}
}
func main() {
stu := newStudent("Codey", 18)
fmt.Println("匿名字段的值:", stu.int)
}
- 在引用相应的值时,只需要
变量.数据类型即可
结构体内嵌结构体
Go语言中没有继承这个概念,结构体的内嵌一般用于处理拥有相同属性的类型,但是Youtube有一个很有趣的Clean Code Struct相关的视频有说过,如果不是必要,最好减少继承。对于一般的情况,比如我们不需要关心代码执行的实行过程如何,只需要调用相同的方法时,我们大可以采用接口实现,用组合的形式,让接口成为规范程序运行的协议,因为接口没有规定过多的程序行为,所以组合会比继承更加轻量。Go的内嵌更类似于组合而不是继承
package main
import "fmt"
type Callable struct{}
func (c Callable) Call() {
fmt.Println("can call")
}
type Photographic struct{}
func (p Photographic) Photograph() {
fmt.Println("can take photos")
}
type Moblie struct {
Callable
Photographic
}
type Pad struct {
Photographic
}
func main() {
var m Moblie
fmt.Println("Moblie Function:")
m.Call()
m.Photograph()
var p Pad
fmt.Println("Pad Function:")
p.Photograph()
}
- 这个例子生动形象地展示了组合的实际状态,我们可以按需组合或内嵌,使得新生成的结构体符合我们的需求
接口
Go语言的接口和其它语言相同,轻量且可以使得程序符合多态的设计。接口的一般定义如下所示
var interface_name interface { //接口类型的变量
Print()// 接口需要实现的函数
}
type interface_name interface { // 自定义接口类型
Print()// 接口需要实现的函数
}
-
Go语言的接口可以被称之为鸭子类型,即“走起来像鸭子,叫起来像鸭子,那么它就是一只鸭子”。换句话说,如果这个类型模拟了接口中需要实现的所有方法,那么我们就说这个类型实现了这个接口。Go语言中的接口有以下两种特性-
只要有类实现(模拟)了接口中包含的所有方法,我们就称这个类型实现了这个接口
-
只要是实现了这个接口的类型,用这个类型定义的变量就可以给这个接口声明的接口赋值
-
-
也就是说,接口类型(interface{})类型的变量,可以接收任何变量的赋值
package main
import (
"fmt"
)
var stuInterface interface {
PrintAge()
}
type Student struct {
Name string
Age int
}
func (s Student) PrintAge() {
fmt.Println(s.Age)
}
func main() {
stuInterface = Student{
Name: "Codey",
Age: 18,
}
stuInterface.PrintAge()
}
- 这里的示例我们定义了一个接口变量,然后让
Student这个结构体模拟了接口的实现,在其后我们就可以直接把Student类型的变量赋值给stuInterface,调用接口中已经定义的函数。运行无任何异常。