六、面向对象
- 仅支持封装,不支持继承和多态
- go语言没有class有struct
- go没有构造器,可以使用工厂方法代替
通过面向对象学习struct和interface
6.1 type关键字
定义一种新的数据类型
func testType() {
// type代表定义一种新的类型,这里等同于int类型的别名。
// 一般用来定义结构体或者接口
type number int
var b number =10
fmt.Println(b)
}
6.2 struct定义和初始化
结构体可以包含多个类型的组合,接近java中的类对象
定义stuct
type treeNode struct {
value int
left, right *treeNode
}
var root treeNode
//创建对象的方法
//1 指定单个参数
root = treeNode{value: 3}
//2. 使用指针创建
root.right = &treeNode{}
//3. 指定全部参数
root.left = &treeNode{5, nil, nil}
//4. new关键字创建
root.right.left = new(treeNode)
//5. 工厂模式创建
root.left.right = treeNodeFactory(2)
//6. 数组定义可以简写
nodes := []treeNode{
{value: 3},
{},
{6, nil, nil},
}
方法属性
- 比普通func多了一个(接收对象 类型),其他和普通函数没有区别
- 通过指针实现值传递
- nil指针可以安全使用,调用方法
//给tree定义一个方法print
//小括号代表this指向括号内接收的对象,node指向对象引用
func (node treeNode) print() {
fmt.Println(node.value)
}
//set pointer实现引用传递
func (node *treeNode) setValue(val int) {
node.value = val
}
值接收 vs 指针接收
- 要改变内容必须使用指针接收
- 结构过大也是用指针接收
- 如果已经使用了指针接收,最好保持一致
package main
import "fmt"
func main(){
p1:=People{
name: "matthew",
age: 33,
gender: false,
}
p1.showPeople()
p1.setName("lisi")
fmt.Println("getName",p1.getName())
}
func testType() {
// type代表定义一种新的类型,这里等同于int类型的别名。
// 一般用来定义结构体或者接口
type number int
var b number =10
fmt.Println(b)
}
// 定义一个people的结构体
type People struct {
name string
age int
gender bool
}
//get 方法
func (this *People)getName() string {
return this.name
}
//set 方法,struct是值传递,所以必须使用指针达成引用传递效果
func (this *People)setName(name string) {
this.name=name
}
//string 方法
func(this *People)showPeople(){
fmt.Println(this)
}
struct的方法无论使用值接收,还是指针接收,调用方式无变化
6.3 接口Interface
- 接口是万能类型,基础类型如int、struct都实现了interface,所以interface{}可以接收任何类型。
- 接口实现是隐式的,实现接口中的所有方法就代表实现了改接口
duck typing:描述事物外部行为而非内部结构,从使用者角度看待。go属于结构化类型系统,接近duck typing
接口定义
- 接口定义了一个方法名字,返回值和参数,不需要方法体
- 接口可以作为参数在函数中传递
- 貌似不能有属性,只能有方法
//接口定义一个方法类型
type Retriver interface {
Get(string) string
}
//函数中使用接口作为参数传递
func download(r Retriver, url string) string {
return r.Get(url)
}
接下来定义接口的实现
- 只要实现了接口的方法就默认实现了接口
- 实现了接口就具备了多态的可能
定义一个type用于mock请求返回一个字符串
package mock
type Retriver struct {
Content string
}
// struct实现了接口的方法,也就实现了接口
func (r Retriver) Get(url string) string {
return r.Content
}
定义一个类似的接口,真实的获取网页的地址
package real
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriver struct {
UserAgent string
Timerout time.Duration
}
func (r Retriver) Get(s string) string {
get, err := http.Get(s)
if err != nil {
panic(err)
}
response, err := httputil.DumpResponse(get, true)
defer get.Body.Close()
if err != nil {
panic(err)
}
return string(response)
}
interface{}万能类型
基础类型int,flat,stuct等都实现了interface{},所以后者可以作为万能类型
func printVal(val interface{}){
fmt.Println(val)
}
type Book struct {
name string
}
func main() {
//interface可以作为万能类型
book:=Book{name: "MyHeart"}
printVal(100)
printVal("string")
printVal(3.14)
printVal(book)
}
判断接口类型
变量.(类型)
func printVal1(val interface{}){
value,ok:=val.(string) //类型判断,是否是string类型,ok代表是string
if ok{
fmt.Println(value)
}else {
fmt.Printf("val is not string,type is %T \n",val)
}
}
func main() {
fmt.Println("-------")
//接口类型判断
printVal1(100)
printVal1("hello")
}
- 表示任何类型: interface{}
- 类型判断 type Assertion
- 类型选择 type Swtich
golang系统接口参考
- Stringer 格式化输出
- Reader
- Writer
6.3 子类继承父类
- 在子类定义时,写入父struct名称,即继承了父类
- 子类可以添加新的属性和方法
- 子类可以直接调用父类的属性和方法
- 可见性仅有首字母大小写控制
package main
import "fmt"
/**
子类继承父类
*/
func main() {
student:=Student{
People: People{
name:"xiaowang",
age:9,
gender:true,
},
grade: 3,
}
student.ShowStudent()
fmt.Println("people name",student.name) //获取父类属性
fmt.Println("student grade",student.grade) //获取父类属性
student.Eating() //调用父类方法
student.study() //调用子类方法
}
type Student struct {
People //子类Student继承父类的属性和方法
grade int //年级
}
//子类增加新的方法
func (s *Student) study() {
fmt.Println("student is studying")
}
func (s *Student)ShowStudent() {
fmt.Println(s)
}
// 定义一个people的结构体
type People struct {
name string
age int
gender bool
}
//get 方法
func (this *People)GetName() string {
return this.name
}
//set 方法,struct是值传递,所以必须使用指针达成引用传递效果
func (this *People)setName(name string) {
this.name=name
}
func (this *People) Eating() {
fmt.Println("People is eating")
}
//string 方法
func(this *People) ShowPeople(){
fmt.Println(this)
}
6.4 多态 interface
golang中的多态概念上和java完全一致,需满足三个条件
- 定义父类对象,go中必须是interface
- 定义子类struct,实现父类全部方法(否则不满足继承条件)
- 父类对象(pointer指针变量)指向(引用)子类变量地址
仔细研究下面代码,体会多态的含义
package main
import "fmt"
/**
1. 定义父类接口
*/
type Animal interface {
GetColor() string
Eat()
Sleep()
}
/**
2.1 定义子类Dog,必须实现所有方法
*/
type Dog struct {
color string
}
func (dog *Dog)Eat() {
fmt.Println("dog is eating")
}
func (dog *Dog) Sleep() {
fmt.Println("dog is sleeping")
}
func (dog *Dog)GetColor() string {
return "dog "+ dog.color
}
/**
2.2 定义子类Cat,同上。两者的方法有不同实现
*/
type Cat struct {
color string
}
func (cat *Cat)Eat() {
fmt.Println("cat is eating")
}
func (cat *Cat)Sleep() {
fmt.Println("cat is sleeping")
}
func (cat *Cat)GetColor() string {
return "cat " + cat.color
}
//多态传参,根据传入的引用类型调用对应方法
func showColor(animal Animal){
fmt.Println(animal.GetColor())
}
func main(){
// 3. 父类指向子类对象
var animal Animal //父类指针
animal=&Cat{color: "white"}
animal.Sleep() //cat is sleeping
showColor(animal)
fmt.Println("-----------")
animal=&Dog{color: "yellow"}
showColor(animal)
animal.Eat() //dog is eating
}