golang学习六:面向对象:继承,对象方法,接口

一、概述:

1. 什么是面向对象:

  • 属性就是特征;
  • 方法就是行为;

2. Go语言中的面向对象:

严格意义上讲, Go语言中没有类(class)的概念, 但是可以将结构体比作类, 因为在结构体中可以添加属性(成员)和方法(函数);

  • 继承: 通过匿名字段实现;
  • 多态: 通过接口实现;
  • 封装: 通过方法实现;

3. 继承:

3.1 匿名字段:

// 父类
type Persion struct {
	id   int
	name string
	age  int
}

// 子类
type Student struct {
	Persion // 继承属性	结构体嵌套

	score int // 添加专属属性
}

func main() {
	// 顺序初始化
	var s1 Student = Student{Persion{101, "小明", 18}, 98}
	fmt.Println(s1) // {{101 小明 18} 98}

	// 自动推导类型
	s2 := Student{Persion{102, "张三", 19}, 90}
	fmt.Println(s2) // {{102 张三 19} 90}

	// 指定初始化: 没有初始化的部分使用默认值
	s3 := Student{score: 100}
	fmt.Println(s3) // {{0  0} 100}

}

3.2 成员操作:


func main() {
	var s1 Student = Student{Persion{101, "小明", 18}, 98}
	// 成员操作 .
	s1.score = 89
	fmt.Println(s1) // {{101 小明 18} 89}

	s1.Persion.id = 110
	fmt.Println(s1) //	{{110 小明 18} 89}

	// 因为父类被继承, 所以不需要.父类
	s1.age = 22

	s1.Persion = Persion{100, "李四", 20}
	fmt.Println(s1)	// {{100 李四 20} 89}
}

3.3 匿名字段同名成员:

子类与父类结构体有相同的成员名时, 默认赋值给子类, 采用就近原则

type Stu struct {
	Persion
	name  string
	score int
}

func main() {
	var s3 Stu
	s3.name = "王五"
	fmt.Println(s3) // {{0  0} 王五 0}

	s3.Persion.name = "wangwu"
	fmt.Println(s3)	// {{0 wangwu 0} 王五 0}
}

3.4 指针类型的匿名字段:

// 指针类型的匿名字段
// 子类
type Student3 struct {
	*Persion
	score int
}

func main() {
	var s Student3
	s.score = 90
	fmt.Println(s) // {<nil> 90}

	// s.id = 100	// err: 内存地址指向为空
	s.Persion = new(Persion)
	s.name = "赵六"
	s.id = 103
	fmt.Println(s)                     // {0xc00000c060 90}
	fmt.Println(s.id, s.name, s.score) // 103 赵六 90

	(*s.Persion).name = "周杰伦"
	fmt.Println(s.id, s.name, s.score) // 103 周杰伦 90

	s.Persion = &Persion{105, "林俊杰", 35}
	fmt.Println(s.id, s.name, s.score)	// 105 林俊杰 90
}

3.5 多重继承:

type Father struct {
	name string
	age  int
	sex  string
}

type Monther struct {
	id   int
	addr string
}

type Son struct {
	Father
	Monther
	score int
}

func main() {
	var son Son

	son.id = 200
	son.addr = "北京"
	son.name = "张三"
	son.age = 18
	son.sex = "男"
	son.score = 100

	fmt.Println(son) // {{张三 18 男} {200 北京} 100}

	// 自动推导
	s1 := Son{Father{"李四", 20, "女"}, Monther{201, "上海"}, 90} // {{李四 20 女} {201 上海} 90}
	fmt.Println(s1)

}
type Humen struct {
	id   int
	name string
}

type Persion struct {
	Humen
	age int
	sex string
}

type Student struct {
	Persion
	score int
}

func main() {
	// 初始化
	var stu Student
	stu.name = "张三"
	stu.age = 20
	stu.score = 94
	fmt.Println(stu) // {{{0 张三} 20 } 94}

	// 自动推导
	stu1 := Student{Persion{Humen{100, "李四"}, 30, "男"}, 98}
	fmt.Println(stu1) // {{{100 李四} 30 男} 98}

}

4. 对象方法:

4.1 创建对象方法和使用:

语法:

// 定义方法
func (对象名 类型)方法名(参数列表) {
	方法体
}
type Cat struct {
	name string
	age  int
}

// 定义方法需要绑定对象
func (c Cat) show() {
	fmt.Println(c.name + "喵喵叫...")
}

type Dog struct {
	name string
	age  int
}

func (d Dog) show() {
	fmt.Println("汪汪叫...")
}

func main() {
	var c Cat
	c.name = "气气"
	c.age = 2
	// 调用对象方法
	c.show() // 气气喵喵叫...

}

4.2 方法的内存模型:

type Student struct {
	id    int
	name  string
	age   int
	score int
}

func (s Student) Display() {
	s.name = "张三"
	fmt.Println(s) // {101 张三 30 -5}
}

// 对象不同, 方法名相同, 不会冲突
// 在方法调用中, 方法接收者是指针类型
// 指针类型、普通类型 表示的是相同对象的类型
func (s *Student) change(age int) {
	s.age = age
}

func main() {
	stu := Student{101, "亚索", 30, -5}
	stu.Display()
	fmt.Println(stu) // {101 亚索 30 -5}
	stu.change(18)
	fmt.Println(stu) // {101 亚索 18 -5}
}

4.3 方法继承:

type Person struct {
	id   int
	name string
	age  int
}

type Student struct {
	Person
	class int // 班级
}

func (p *Person) PrintInfo() {
	fmt.Printf("编号: %d\n", p.id)
	fmt.Printf("姓名: %s\n", p.name)
	fmt.Printf("年龄: %d\n", p.age)
}

func main() {
	p := Person{110, "张三", 18}
	p.PrintInfo()
	// 编号: 110
	// 姓名: 张三
	// 年龄: 18

	s := Student{Person{111, "李四", 20}, 2}
	// 子类可以继承父类的属性和方法
	s.PrintInfo()
	// 编号: 111
	// 姓名: 李四
	// 年龄: 20
}

4.4 方法重写:

方法重写之后使用的是子类的方法

4.5 方法值和方法表达式:

  • 方法表达式需要显示传参;

5. 接口:

5.1 接口的定义和使用:

package main

import "fmt"

// 定义接口: 一般以er结尾, 根据接口实现功能
type Humaner interface {
	// 方法:仅仅是方法的声明
	sayhi()
}

type Student struct {
	name  string
	age   int
	score int
}

func (s *Student) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我的成绩是%d分\n", s.name, s.age, s.score)
}

type Teacher struct {
	name    string
	age     int
	subject string
}

func (t *Teacher) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我教的科目是%s\n", t.name, t.age, t.subject)
}

func main() {
	// 定义一个接口变量
	// 接口是一种数据类型, 可以接受满足对象的信息
	// 接口是虚的, 方法是实的
	// 接口定义规则, 方法实现规则
	// 接口中定义的规则, 对象中必须有实现
	var h Humaner

	// 创建对象
	stu := Student{"小明", 18, 98}
	// stu.sayhi() // 我是小明, 今年18岁, 我的成绩是98分

	// 将对象信息赋值给接口类型变量
	h = &stu
	h.sayhi() // 我是小明, 今年18岁, 我的成绩是98分

	tea := Teacher{"老王", 30, "物理"}
	// tea.sayhi() // 我是老王, 今年30岁, 我教的科目是物理

	h = &tea
	h.sayhi()
}

5.2 多态的实现:

package main

import "fmt"

type Humaner interface {
	sayhi()
}

type Student struct {
	name  string
	age   int
	score int
}

func (s *Student) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我的成绩是%d分\n", s.name, s.age, s.score)
}

type Teacher struct {
	name    string
	age     int
	subject string
}

func (t *Teacher) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我教的科目是%s\n", t.name, t.age, t.subject)
}

// 多态的实现
// 将接口作为函数参数, 实现多态
func sayhello(h Humaner) {
	h.sayhi()
}

func main() {
	stu := Student{"小明", 18, 98}

	// 调用多态
	sayhello(&stu) // 我是小明, 今年18岁, 我的成绩是98分

	tea := Teacher{"老王", 30, "物理"}
	// 调用多态
	sayhello(&tea) // 我是老王, 今年30岁, 我教的科目是物理

}

5.3 接口继承与转换:

package main

import "fmt"

type Humaner interface {
	sayhi()
}

type Personer interface { // 超集
	Humaner // 匿名字段
	sing(string)
}

type Student struct {
	name  string
	age   int
	score int
}

func (s *Student) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我的成绩是%d分\n", s.name, s.age, s.score)
}
func (s *Student) sing(n string) {
	fmt.Printf("我是%s, 我为大家唱首歌: %s\n", s.name, n)
}

type Teacher struct {
	name    string
	age     int
	subject string
}

func (t *Teacher) sayhi() {
	fmt.Printf("我是%s, 今年%d岁, 我教的科目是%s\n", t.name, t.age, t.subject)
}
func (t *Teacher) sing(n string) {
	fmt.Printf("我是%s, 我为大家唱首歌: %s\n", t.name, n)
}

func main0901() {
	// 定义接口
	var h Humaner

	stu := Student{"小明", 18, 98}
	h = &stu
	h.sayhi() // 我是小明, 今年18岁, 我的成绩是98分

	// 定义接口变量
	var p Personer
	p = &stu
	p.sing("小星星") // 我是小明, 我为大家唱首歌: 小星星

	// tea := Teacher{"老王", 30, "物理"}
}

func main() {
	// 接口的转换
	// 接口类型变量定义
	var h Humaner  // 子集
	var p Personer // 超集

	var stu Student = Student{"小吴", 18, 59}

	p = &stu
	// 将一个接口赋值给另一个接口
	// 超集中包含所有子集的方法
	h = p     // ok
	h.sayhi() // 我是小吴, 今年18岁, 我的成绩是59分

}

5.4 空接口:

  • 接口类型可以接收任何类型的数据;
func main() {
	var i interface{}
	// 接口类型
	fmt.Println(i)        // <nil>
	fmt.Printf("%T\n", i) // <nil>

	i = 10
	fmt.Println(i)        // 10
	fmt.Printf("%T\n", i) // int

	i = 3.14
	fmt.Println(i)        // 3.14
	fmt.Printf("%T\n", i) // float64
}

接收任意类型的数据

func test() {
	fmt.Println("test...")
}
func main() {
	// 定义空接口类型的切片
	var i []interface{}
	fmt.Printf("%T\n", i)
	i = append(i, 10, 3.14, "aaa", test)
	fmt.Println(i) // [10 3.14 aaa 0x109eb30]

	for idx := 0; idx < len(i); idx++ {
		fmt.Println(i[idx])
		// 10
		// 3.14
		// aaa
		// 0x109eb30
	}
}

5.5 类型断言:

interface的变量里面可以存储任意类型的数值. 那么如何反向知道这个变量里面实际保存了哪个类型的对象呢:

  • comma-ok断言;
  • switch测试;

Go语言里面有一个语法, 可以直接判断是否是该类型的变量: value, ok = element.(T), 这里value就是变量的值, ok是一个bool类型, element是interface变量, T是断言的类型;
如果element里面确实是存储了T类型的数值, 那么OK返回true, 否则返回false;

  • comma-ok断言:
func demo() {
	fmt.Println("hello...")
}
func main() {
	// 类型断言
	var i []interface{}
	i = append(i, 10, 3.14, "aaa", demo)
	fmt.Println(i) // // [10 3.14 aaa 0x109cfb0]

	for _, v := range i {
		fmt.Println(v)
		if data, ok := v.(int); ok {
			fmt.Println("整型数据", data)
		} else if data, ok := v.(float64); ok {
			fmt.Println("浮点数据", data)
		} else if data, ok := v.(string); ok {
			fmt.Println("字符串数据", data)
		} else if data, ok := v.(func()); ok {
			// 函数调用
			data()
		}
		// 10
		// 整型数据 10
		// 3.14
		// 浮点数据 3.14
		// aaa
		// 字符串数据 aaa
		// 0x109cfb0
		// hello...
	}
}
  • switch测试:
func main() {
	var i []interface{}
	i = append(i, 10, 3.14, "aaa", demo)

	for _, data := range i {
		switch value := data.(type) {
		case int:
			fmt.Println("整型数据", value)
		case float64:
			fmt.Println("浮点型数据", value)
		case string:
			fmt.Println("字符串数据", value)
		case func():
			value()
		default:
			fmt.Println("类型未知...", value)
		}
	}
	// 整型数据 10
	// 浮点型数据 3.14
	// 字符串数据 aaa
	// hello...
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅弋、璃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值