<>Go面向对象编程

<>匿名字段

* 匿名字段, 从名字来看, 就是没有名称 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") }

技术
©2019-2020 Toolsou All rights reserved,
Vue.js入门(五)---在vue中使用echarts词云Pandas统计分析基础_数据处理(DataFrame常用操作)element UI dialog点击dialog区域外会关闭dialog应届毕业生看过来!Java面试经典77问,看完离工作就不远了关于蓝桥杯大赛,你应该了解的那些事!mysql 分区-key分区(五)海康威视-嵌入式软件笔试题PHP Redis 监听过期的 key 事件C语言循环语句笔记详解以及练习-折半查找算法、猜数字游戏JVM概述