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



