Go面向对象编程

Go面向对象编程

Go面向对象编程_第1张图片

匿名字段

  • 匿名字段, 从名字来看, 就是没有名称
package main

import "fmt"

type Person struct {
	age int
	name string
	sex bool
}

type MPerson struct {
	Person // 匿名字段
	id int
}

func main() {
	var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
	fmt.Println(mperson)
}
  • 匿名字段, 可以理解成MPerson继承了Person的字段
  • 匿名字段的输出:
fmt.Println(mperson.Person)
  • 匿名字段甚至可以是非结构体数据类型, 输出的形式同结构体匿名字段的输出, 见代码
package main

import "fmt"

type Person struct {
	age int
	name string
	sex bool
}

type MPerson struct {
	Person
	int
}

func main() {
	var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
	fmt.Println(mperson.Person, mperson.int)
}
  • 匿名字段还可以是指针, 指针类型的初始化之前的文章中已经说过了, 直接看代码
package main

import "fmt"

type Person struct {
	age int
	name string
	sex bool
}

type MPerson struct {
	*Person
	int
}

func main() {
	var mperson MPerson = MPerson{&Person{18, "xiaohua", true}, 1}
	fmt.Println(*(mperson.Person), mperson.int)
}

同名字段

  • 看代码
package main

import "fmt"

type Person struct {
	age int
	name string
	sex bool
}

type MPerson struct {
	Person
	age int
}

func main() {
	var mperson MPerson = MPerson{Person{18, "xiaohua", true}, 1}
	fmt.Println(mperson)
}
  • 可以发现MPerson的age和Person中的age同名, 通过代码可以发现, 实际上这2个同名字段并不是一个字段
  • 可以联系C++的封装来进行理解
  • 那么如果我们现在输出MPerson的age字段, 输出的是哪个age呢?
  • 实际上输出的就是MPerson的age即上面代码中的1

注:
如果想要输出MPerson中的Person中的age
可以这么写:
fmt.Println(mperson.Person.age)
可以发现Go和C++还是有区别的, 此处是Person.age, 而不是Person::age, 注意之

方法

  • 方法其实就是面向对象中的成员函数, 先来看代码
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println(obj.age)
}

func main() {
	person := Person{18, 1}
	person.f()
}


注:
func (obj Person)f()
表示这是关联到Person的方法, 之后的调用就可以通过Person对象来通过.来使用该方法

  • 事实上, 编译器内部会帮你自动转化指针和对象, 哪怕你只有一个针对于对象调用的方法, 你用指针去调用也是可以的, 如下:
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println("hello")
}

func main() {
	p := &Person{18, 1}
	p.f() // 用指针去调用也是可以的
}
  • 之前说过要想在方法中改struct中的value, 必须传递指针, 由于编译器会帮我们自动转换, 所以我们看看我们用指针去调用针对于对象的方法能不能改
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	obj.age = 10
}

func main() {
	p := &Person{18, 1}
	p.f()
	fmt.Println(p)
}

注:
答案是改不了
因为编译器先把p转化成*p, 再去调用f(), 这就是值传递语义, 所以改不了, 注意

方法的继承

  • 继承
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println("hello")
}

type MPerson struct {
	Person
	name string
}

func main() {
	p := MPerson{Person{18, 1}, "xiaohua"}
	p.f()
}

注:
MPerson内部包含了Person的匿名字段, 不仅继承了成员, 也继承了方法

  • 重写
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println("hello")
}

type MPerson struct {
	Person
	name string
}

func (obj MPerson)f() {
	fmt.Println("hello everyone")
}

func main() {
	p := MPerson{Person{18, 1}, "xiaohua"}
	p.f()
}

注:
子类重写了父类的方法, 子类在调用的时候默认先从自己的作用域去找方法, 找不到再去父类作用域找
如果想调用父类中的同名的方法, 可以这么写:
p.Person.f()

方法值

  • 先看代码
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println("hello")
}

func main() {
	p := Person{18, 1}
	function := p.f
	function()
}
  • function := p.f这句话其实是保存了struct中方法的入口, 可以类似于函数指针来理解

方法表达式

  • 看代码
package main

import "fmt"

type Person struct {
	age int
	id int
}

func (obj Person)f() {
	fmt.Println("hello")
}

func main() {
	p := Person{18, 1}
	function := (Person).f
	function(p)
}
  • function := (Person).f就是方法表达式, 之后调用的时候需要传递对应的变量进去
  • 这个时候我要是想传递指针进去还可以像之前那样成功调用嘛?答案是不行的,这里是严格的类型检查

接口

  • 接口类型是一种抽象的类型, 它不会暴露出自己所代表的对象的内部的结构和方法集合, 因此接口类型不能实例化
  • 接口只有方法声明, 没有方法实现, 没有数据字段
  • 接口的定义类似于:
type 接口的类型名 interface {
	方法名
}
  • 接口类型的变量可以被任何实现了内部方法的对象来赋值, 其实, Go的接口实现了类似于C++多态的功能
package main

import "fmt"

type Inter interface {
	f(data int)
}

type Person struct {
	age int
	name string
}

func (person Person)f(data int) {
	fmt.Println(data)
}

func main() {
	var myinterface Inter
	p := Person{18, "xiaohua"}
	myinterface = p
	myinterface.f(10)
}

注:
如果想将对象赋值给接口, 必须这个对象实现了接口的全部方法, 否则会报错

  • 接口的继承, 在接口中加被继承的接口的匿名字段即可
type Inter interface {
	f(data int)
}

type Int2er interface {
	Inter
	f2()
}

注:
在这段代码中, Inter就是子集, Int2er就是超集
超集可以转化成子集, 反过来错误
可以用C++的多态来类比理解,
超集理解成子类, 子集理解成父类, 转化可以理解成赋值操作

空接口

  • 空接口, 顾名思义, 就是内部什么方法都没有, 但是空接口可以接收任意类型的对象值, 见代码
package main

import "fmt"

func main() {
	var i interface{} = 1
	fmt.Println(i)

	i = "hello"
	fmt.Println(i)
}

注:
可见, 空接口可以接收任意类型的值

  • 空接口的用处: 由于空接口可以接收任意类型的值, 所以和可变参数结合后会有奇效
package main

import "fmt"

func print(args... interface{}) {
	for _, data := range args {
		fmt.Println(data)
	}
}

func main() {
	print(1, "hello")
}

你可能感兴趣的:(go语言学习笔记,go)