引言
Go 的面向对象,语法清奇,思路独特,和传统的面向对象语言如 Java 完全不同,可开阔思路。
结构体
谈到面向对象,就不得不提其载体 struct
type 结构体名称 struct{
field1 type
field2 type
}
以上便是其定义的语法,使用也很简单
type Student struct {
Name,Address string
Age int
Score float64
}
定义了个 Student 的结构体,里面有几个常用的字段。以下为引用的一种方法
alice := Student{"molaifeng", "北京", 32, 99.5}
方法
在 Go 中,方法是指定在数据类型上的,因此如果给上面的 student 结构体指定特有的方法,只需如此做
func (this Student) Speak(str string) {
fmt.Println(str);
}
使用刚刚定义的方法
func main() {
alice.Speak("My name is Alice")
}
面向对象三大特性之封装
其实这个特性和上述介绍的方法一致,因为方法对外提供接口并隐藏实现细节。
面向对象三大特性之继承
继承可以解决代码复用,当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如刚才的Student),在该结构体中定义这些相同的属性和方法。
type Pupil struct {
Student
}
type Graduate struct {
Student
}
当然了,结构体也可以实现多重继承,这里就不展开介绍了。
接口
介绍多态之前,还得宕开一笔说下接口,因为这个是实现多态的基础。
interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。到某个自定义类型(比如结构体 Student)要使用的时候,在根据具体情况把这些方法写出来。
type 接口名 interface {
method1(参数列表) 返回值列表
method2(参数列表) 返回值列表
}
func (t 自定义类型) method1(参数列表) (返回值列表) {
//方法实现
}
func (t 自定义类型) method2(参数列表) (返回值列表) {
//方法实现
}
需要说明的是,Go 中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Go 中没有 implements 这样的关键字。
type Repeat interface {
Say()
}
func (s *Student) Say() {
fmt.Println(s.Name)
}
func main() {
var re Repeat = alice
re.Say()
}
还有另一个重要的特点,空接口 interface{} 没有任何方法,所以所有类型都实现了空接口。
type T interface {
}
func main() {
// Stutent 默认实现了空接口
var t T = alice
fmt.Println(t)
var t1 interface{} = alice
num := 8.8
fmt.Println(t1)
t1 = num
fmt.Println(t1)
}
接口和继承
package main
import "fmt"
type Monkey struct {
Name string
}
func (this *Monkey) climbTree() {
fmt.Println(this.Name, " 生来会爬树")
}
type LittleMonkey struct {
Monkey
}
type BirdAble interface {
Flying()
}
type FishAble interface {
Swimming()
}
func (this *LittleMonkey) Flying() {
fmt.Println(this.Name, " 通过学习会飞翔")
}
func (this *LittleMonkey) Swimming() {
fmt.Println(this.Name, " 通过学习会游泳")
}
func main() {
monkey := LittleMonkey{
Monkey{Name:"悟空"},
}
monkey.climbTree()
monkey.Flying()
monkey.Swimming()
}
接口比继承更加灵活,继承是满足 is a 的关系,而接口只需满足 like a 的关系。
多态
变量(实例)具有多种形态。这里给出个判断变量类型的实例,来了解多态。
package main
import "fmt"
type Student struct {
Name string
Score float64
}
func JudgeType(items ...interface{}) {
for index, x := range items {
switch x.(type) {
case bool :
fmt.Printf("第 %v 个参数是 bool 类型,值是 %v \n", index, x)
case float32 :
fmt.Printf("第 %v 个参数是 float32 类型,值是 %v \n", index, x)
case float64 :
fmt.Printf("第 %v 个参数是 float64 类型,值是 %v \n", index, x)
case byte:
fmt.Printf("第 %v 个参数是 byte 类型,值是 %v \n", index, x)
case int, int32, int64:
fmt.Printf("第 %v 个参数是 整数类型,值是 %v \n", index, x)
case string:
fmt.Printf("第 %v 个参数是 string 类型,值是 %v \n", index, x)
case Student:
fmt.Printf("第 %v 个参数是 Student 类型,值是 %v \n", index, x)
case *Student:
fmt.Printf("第 %v 个参数是 *Student 类型,值是 %v \n", index, x)
default :
fmt.Printf("第 %v 个参数是 类型 不确定,值是 %v \n", index, x)
}
}
}
func main() {
var n1 float32 = 1.1
var n2 float64 = 1.1
var n3 int32 = 1
address := "北京"
var b byte = 'a'
s1 := Student{}
s2 := &Student{}
JudgeType(n1, n2, n3, address, b, s1, s2)
}
之所以接口可以实现类型断言,是因为在上面接口介绍中,说了一个注意点,那就是空接口 interface{} 没有任何方法,所以所有类型都实现了空接口。
最后再给个实例
package main
import "fmt"
type Usb interface {
Start()
Stop()
}
type Phone struct {
Name string
}
func (p Phone) Start() {
fmt.Println(p.Name, "手机开始工作")
}
func (p Phone) Stop() {
fmt.Println(p.Name, "手机停止工作")
}
func (p Phone) Call() {
fmt.Println(p.Name, "手机开始打电话")
}
type Camera struct {
Name string
}
func (c Camera) Start() {
fmt.Println(c.Name, "相机开始工作")
}
func (c Camera) Stop() {
fmt.Println(c.Name, "相机停止工作")
}
type Computer struct {
Name string
}
// 编写一个方法 working 方法,接收一个 usb 接口类型变量
// 只要是实现了 Usb 接口 (所谓实现 Usb 接口,就是指实现了 Usb 接口声明所有方法)
func (c Computer) Working(usb Usb) {
usb.Start()
if p, ok := usb.(Phone); ok { // 关键点在这
p.Call()
}
usb.Stop()
}
func main() {
var computer Computer
var usb [3]Usb
usb[0] = Phone{"苹果"}
usb[1] = Phone{"联想"}
usb[2] = Camera{"尼康"}
for _, v := range usb{
computer.Working(v)
}
}