类的定义和初始化

GO语言没有以往面向对象语言中的class 关键字,而是通过结构体来定义类

  1. type Student struct {
  2. id uint
  3. name string
  4. male bool
  5. score float64
  6. }

Go 语言中也不支持构造函数、析构函数,取而代之地,可以通过定义形如 NewXXX 这样的全局函数(首字母大写)作为类的初始化函数:

  1. func NewStudent(id uint, name string, male bool, score float64) *Student {
  2. return &Student{id, name, male, score}
  3. }

在这个函数中,我们通过传入的属性字段对 Student 类进行初始化并返回一个指向该类的指针,除此之外,还可以初始化指定字段:

  1. func NewStudent(id uint, name string, score float64) *Student {
  2. return &Student{id: id, name:name, score:score}
  3. }

在 Go 语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如 bool 类型的零值为 falseint 类型的零值为 0,string 类型的零值为空字符串,float 类型的零值为 0.0。

然后我们可以在 main() 函数中调用这个 NewStudent 函数对 Student 类进行初始化:

  1. student := NewStudent(1, "学院君", 100)
  2. fmt.Println(student)

添加成员方法

由于 Go 语言不支持 class 这样的代码块,要为 Go 类添加成员方法,需要在 func 和方法名之间添加方法所属的类型声明(有的地方将其称之为接收者声明),以 Student 类为例,要为其添加返回 name 值的方法,可以这么做:

  1. func (s Student) GetName() string {
  2. return s.name
  3. }

然后我们就可以在初始化 Student 后,通过 GetName() 方法获取 name 值:

  1. student := NewStudent(1, "学院君", 100)
  2. fmt.Println("Name:", student.GetName())

可以看到,我们通过在函数中增加接收者声明的方式定义了函数所归属的类型,这个时候,函数就不再是普通的函数,而是类的成员方法了,然后可以在成员方法中,通过声明的类型变量来访问类的属性和其他方法(Go 语言不支持隐藏的 this 指针,所有的东西都是显式声明)。

上述方法是一个只读方法,如果我们要在外部通过 Student 类暴露的方法设置 name 值,可以这么做:

  1. func (s *Student) SetName(name string) {
  2. s.name = name
  3. }

你可能已经注意到,这里的方法声明和前面 GetXXX 方法声明不太一样,Student 类型设置成了指针类型:

  1. s *Student

这是因为 Go 语言面向对象编程不像 PHP、Java 那样支持隐式的 this 指针,所有的东西都是显式声明的,在 GetXXX 方法中,由于不需要对类的成员变量进行修改,所以不需要传入指针,而 SetXXX 方法需要在函数内部修改成员变量的值,并且作用到该函数作用域以外,所以需要传入指针类型(结构体是值类型,不是引用类型,所以需要显式传入指针)。

注:我们可以把接收者类型为指针的成员方法叫做指针方法,把接收者类型为非指针的成员方法叫做值方法。

接下来,我们可以在 main 函数中初始化 Student 类之后,通过 SetName 方法修改 name 值,然后再通过 GetName 将其打印出来:

  1. student := NewStudent(1, "学院君", 100)
  2. student.SetName("学院君小号")
  3. fmt.Println("Name:", student.GetName())

打印结果是:

  1. Name: 学院君小号

PHP、Java 支持默认调用类的 toString 方法以字符串格式打印类的实例,Go 语言也有类似的机制,只不过这个方法名是 String,以上面这个 Student 类型为例,我们为其编写 String 方法如下:

  1. func (s Student) String() string {
  2. return fmt.Sprintf("{id: %d, name: %s, male: %t, score: %f}",
  3. s.id, s.name, s.male, s.score)
  4. }

然后我们可以在 main 方法中这样调用来打印类实例:

  1. student := NewStudent(1, "学院君", 100)
  2. fmt.Println(student)

无需显式调用 String 方法,Go 语言会自动调用该方法来打印,结果如下:

  1. {id: 1, name: 学院君, male: false, score: 100.000000}

一个数据类型关联的所有方法,共同组成了该类型的方法集合。同一个方法集合中的方法不能出现重名,并且,如果它们所属的是一个结构体类型,那么它们的名称与该类型中任何字段的名称也不能重复。