byte数组初始化_Go语⾔⾯向对象教程——类的定义、初始化
和成员⽅法
先看看 Go 中的类型系统。
对于⾯向对象编程的⽀持,Go 语⾔的实现可以说是完全颠覆了以往我们对⾯向对象编程的认知,下⾯我们先通过对 Go 语⾔类型系统的介绍让你有⼀个整体的认知。
Go 语⾔⾯向对象编程设计得简洁⽽优雅。
简洁之处在于,Go 语⾔并没有沿袭传统⾯向对象编程中的诸多概念,⽐如类的继承、接⼝的实现、构造函数和析构函数、隐藏的 this 指针等,也没有 public、protected、private 之类的可见性修饰符。
优雅之处在于,Go 语⾔对⾯向对象编程的⽀持是语⾔类型系统中的天然组成部分,整个类型系统通过接⼝串联,浑然⼀体。
很少有编程类的书谈及类型系统这个话题,实际上类型系统才是⼀门编程语⾔的地基,它的地位⾄关重要。因此,这⾥我们将从类型系统⼊⼿介绍 Go 语⾔的⾯向对象编程特性。
顾名思义,类型系统是指⼀个语⾔的类型体系结构。⼀个典型的类型系统通常包含如下基本内容:
基础类型,如 byte、int、bool、float、string 等;
复合类型,如数组、切⽚、 字典、结构体、指针等;
可以指向任意对象的类型(Any 类型);
值语义和引⽤语义;
⾯向对象,即所有具备⾯向对象特征(⽐如成员⽅法)的类型;
接⼝。
类型系统描述的是这些内容在⼀个语⾔中如何被关联。因为 Java 语⾔⾃诞⽣以来被称为最纯正的⾯向对象语⾔,所以我们就先以 Java 语⾔为例讲⼀讲类型系统。
在 Java 语⾔中,存在两套完全独⽴的类型系统:⼀套是值类型系统,主要是基本类型,如 byte、int、boolean、char、double 等,这些类型基于值语义;⼀套是以 Object 类型为根的对象类型系统,这些类型可以定义成员变量和成员⽅法,可以有虚函数,基于引⽤语义,只允许在堆上创建(通过使⽤关键字 new)。Java 语⾔中的 Any 类型就是整个对象类型系统的根 —— java.lang.Object 类型,只有对象类型系统中的实例才可以被 Any类型引⽤。值类型想要被 Any 类型引⽤,需要装箱 (boxing)过程,
⽐如 int 类型需要装箱成为 Integer 类型。另外,只有对象类型系统中的类型才可以实现接⼝,具体⽅法是让该类型从要实现的接⼝继承。
相⽐之下,Go 语⾔中的⼤多数类型都是值语义,并且都可以包含对应的操作⽅法。在需要的时候,你可以给任何类型(包括内置类型)增加新⽅法。⽽在实现某个接⼝时,⽆需从该接⼝继承(事实上,Go 语⾔根本就不⽀持⾯向对象思想中的继承、实现语法),只需要实现该接⼝要求的所有⽅法即可。任何类型都可以被 Any 类型引⽤。在 Go 语⾔中,Any 类型就是空接⼝,即 interface{}。
定义数组初始化注:这⾥的值语义和引⽤语义等价于之前介绍类型时提到的值类型和引⽤类型。
接下来我们会对 Go 语⾔类型系统的特点逐⼀进⾏讲解。
声明:Go 语⾔类型系统篇内容节选⾃⾃《Go 语⾔编程》(许式伟等著)中类型系统⼀节。
接下来,我们就与 Go 语⾔⾯向对象编程相关的特性展开介绍。
类的定义和初始化
Go 语⾔的⾯向对象编程与我们之前所熟悉的 PHP、Java 那⼀套完全不同,没有 class、extends、implements之类的关键字和相应的概念,⽽是借助结构体来实现类的声明,⽐如要定义⼀个学⽣类,可以这么做:
type Student struct { id uint name string male bool score float64}
类名为 Student,并且包含了 id、name、male、score 四个属性,Go 语⾔中也不⽀持构造函数、析构函数,取⽽代之地,可以通过定义形如 NewXXX 这样的全局函数(⾸字母⼤写)作为类的初始化函数:
func NewStudent(id uint, name string, male bool, score float64) *Student { return &Student{id, name, male, score}}
在这个函数中,我们通过传⼊的属性字段对 Student 类进⾏初始化并返回⼀个指向该类的指针,除此之外,还可以初始化指定字段:
func NewStudent(id uint, name string, score float64) *Student { return &Student{id: id, name:name, score:score}}
在 Go 语⾔中,未进⾏显式初始化的变量都会被初始化为该类型的零值,例如 bool 类型的零值为 false,int 类型的零值为 0,string 类型的零值为空字符串,float 类型的零值为 0.0。
然后我们可以在 main() 函数中调⽤这个 NewStudent 函数对 Student 类进⾏初始化:
student := NewStudent(1, "学院君", 100)fmt.Println(student)
上述代码的打印结果如下:
&{1 学院君 false 100}
为类添加成员⽅法
由于 Go 语⾔不⽀持 class 这样的代码块,要为 Go 类添加成员⽅法,需要在 func 和⽅法名之间添加⽅法所属的类型声明(有的地⽅将其称之为接收者声明),以 Student 类为例,要为其添加返回 name 值的⽅法,可以这么做:
func (s Student) GetName() string { return s.name}
然后我们就可以在初始化 Student 后,通过 GetName() ⽅法获取 name 值:
student := NewStudent(1, "学院君", 100)fmt.Println("Name:", student.GetName())
可以看到,我们通过在函数中增加接收者声明的⽅式定义了函数所归属的类型,这个时候,函数就不再是普通的函数,⽽是类的成员⽅法了,然后可以在成员⽅法中,通过声明的类型变量来访问类的属性和其他⽅法(Go 语⾔不⽀持隐藏的 this 指针,所有的东西都是显式声明)。
上述⽅法是⼀个只读⽅法,如果我们要在外部通过 Student 类暴露的⽅法设置 name 值,可以这么做:
func (s *Student) SetName(name string) { s.name = name}
你可能已经注意到,这⾥的⽅法声明和前⾯ GetXXX ⽅法声明不太⼀样,Student 类型设置成了指针类型:
s *Student
这是因为 Go 语⾔⾯向对象编程不像 PHP、Java 那样⽀持隐式的 this 指针,所有的东西都是显式声明的,在 GetXXX ⽅法中,由于不需要对类的成员变量进⾏修改,所以不需要传⼊指针,⽽ SetXXX ⽅法需要在函数内部修改成员变量的值,并且作⽤到该函数作⽤域以外,所以需要传⼊指针类型(结构体是值类型,不是引⽤类型,所以需要显式传⼊指针)。
注:我们可以把接收者类型为指针的成员⽅法叫做指针⽅法,把接收者类型为⾮指针的成员⽅法叫做值⽅法。
接下来,我们可以在 main 函数中初始化 Student 类之后,通过 SetName ⽅法修改 name 值,然后再通过 GetName 将其打印出来:
student := NewStudent(1, "学院君", 100)student.SetName("学院君⼩号")fmt.Println("Name:", student.GetName())
打印结果是:
Name: 学院君⼩号
PHP、Java ⽀持默认调⽤类的 toString ⽅法以字符串格式打印类的实例,Go 语⾔也有类似的机制,只不过这个⽅法名是 String,以上⾯这个 Student 类型为例,我们为其编写 String ⽅法如下:
func (s Student) String() string { return fmt.Sprintf("{id: %d, name: %s, male: %t, score: %f}", s.id, s.name, s.male, s.score)}
然后我们可以在 main ⽅法中这样调⽤来打印类实例:
student := NewStudent(1, "学院君", 100)fmt.Println(student)
⽆需显式调⽤ String ⽅法,Go 语⾔会⾃动调⽤该⽅法来打印,结果如下:
{id: 1, name: 学院君, male: false, score: 100.000000}
⼀个数据类型关联的所有⽅法,共同组成了该类型的⽅法集合。同⼀个⽅法集合中的⽅法不能出现重名,并且,如果它们所属的是⼀个结构体类型,那么它们的名称与该类型中任何字段的名称也不能重复。
除了基于结构体定义的⾃定义类之外,Go 语⾔还⽀持为任何类型添加成员⽅法,包括基本类型,下⼀篇我们将演⽰如何给前⾯数据类型系列中介绍的基本类型和复合类型添加成员⽅法,实现类似 Java 的「装箱」(boxing)功能。
推荐阅读
猜猜看go是不是⾯向对象语⾔?能不能⾯向对象编程?
喜欢本⽂的朋友,欢迎关注“Go语⾔中⽂⽹”:
Go语⾔中⽂⽹启⽤学习交流,欢迎加:274768166
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论