基于GoFrame框架的Go语言语法学习-2

方法集:

go语言中,每个类型都有与之相关的方法,把这个类型所有方法称为类型的方法集。

package main
import (
        "fmt"
)
type Student struct {
        age int8
        name string
}
func (s Student) showName() {
        fmt.Println(s.name)
}
func (s *Student) setName(newName string) {
        s.name = newName
}
类型Student方法集包含了showName()方法。

类型*Student方法集包含了showName()方法和setName()方法

因为:

  类型 T 方法集包含全部 receiver T 方法。(类型Student方法集包含了receiver Student)方法
  类型 *T 方法集包含全部 receiver T + *T 方法 (类型*Student方法集包含了receiver *Student)方法
  为啥?
个人理解,是基于Go语言特性,Go语言在方法调用时,如果方法的接受器是一个指针,即使传的值不是指针类型的,也会自动转换为指针类型。哎?这不就是上面那两个的意思吗!!!

方法集和嵌入

嵌入,即把一个类型作为另外一个类型匿名字段。如下:

type Person struct {
	age  int8
	name string
}

type Student struct {
	// 嵌入Person类型
    // 匿名字段
	Person
}
package main
import "fmt"

type Person struct {
	age int8
	name string
}
func (p Person) showName(){
	fmt.Println(p.name)
}

func (p *Person) setName(newName string){
	p.name = newName
}
type Student1 struct {
	// 值类型的嵌入
	// Student1包含了Person,那么Student1对应的value和pointer包含了Person
	Person
}

type Student2 struct {
	// 指针类型的嵌入
	*Person
}
func main(){
	// 内嵌类型Person默认值为Person{age: 0, name:""}
	s1 := Student1{}
	s1.setName("student1_01")
	s1.showName()

	// 内嵌类型*Person默认值为nil
	// Error,由于目前内嵌类型的值为nil,会触发报错
	//s2 := &Student2{}
	//s2.setName("student1_02")
	//s2.showName()
	s3 := &Student2{Person: &Person{age: 3, name: "s3"}}
	s3.showName()
}

因为:

嵌入和方法集的关系

● 类型S包含匿名字段T,则S和*S方法集包含T方法。// Student1包含匿名字段Person

● 类型S包含匿名字段*T,则S和*S方法集包含T + *T方法。// Student2包含匿名字段*Person

● 不管嵌入的是T还是*T,*S方法集包含T + *T方法。 

匿名字段

go支持只提供类型不写字段名的方式,也就是匿名字段,也称为嵌入字段

// 同名字段的情况
package main

import "fmt"

//人
type Person struct {
    name string
    sex  string
    age  int
}

type Student struct {
    // `只提供类型`而`不写字段名`
    Person
    id   int
    addr string
    //同名字段
    name string
}

func main() {
    var s Student
    // 给自己字段赋值了
    s.name = "5lmh"
    fmt.Println(s)

    // 若给父类同名字段赋值,如下
    s.Person.name = "枯藤"
    fmt.Println(s)
}

//     {{  0} 0  5lmh}
//     {{枯藤  0} 0  5lmh}

所有的内置类型和自定义类型都是可以作为匿名字段去使用

package main
import "fmt"
// 人
type Person struct {
   name string
   sex  string
   age  int
}
// 自定义类型
type mystr string
// 学生
type Student struct {
   Person
   int
   mystr
}
func main() {
   s1 := Student{Person{"5lmh", "man", 18}, 1, "bj"}
   fmt.Println(s1)
}

指针类型匿名字段

// 人
type Person struct {
   name string
   sex  string
   age  int
}

// 学生
type Student struct {
   *Person
   id   int
   addr string
}

func main() {
   s1 := Student{&Person{"5lmh", "man", 18}, 1, "bj"}
   fmt.Println(s1)
   fmt.Println(s1.name)
   fmt.Println(s1.Person.name)
}

// {0xc00007e4b0 1 bj}
// 5lmh
// 5lmh

接口

接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

1.1 接口

1.1.1. 接口类型

在Go语言中接口(interface)是一种类型,一种抽象的类型(多抽象?)。

interface是一组method的集合,是duck-type programming的一种体现。

接口做的事情就像是定义一个协议(规则):只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。---- 只要能干活,我就要,怎么干,我不管。

牢记接口(interface)是一种类型。

1.1.2 为啥使用接口?

type Cat struct{}
func (c Cat) Say() string { return "喵喵喵" }

type Dog struct{}
func (d Dog) Say() string { return "汪汪汪" }

func main() {
    c := Cat{}
    fmt.Println("猫:", c.Say())
    d := Dog{}
    fmt.Println("狗:", d.Say())
}

上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?

像类似的例子在我们编程过程中会经常遇到:

比如一个网上商城可能使用支付宝、微信、银联等方式去在线支付,我们能不能把它们当成“支付方式”来处理呢?

比如三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?

比如销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?

Go语言中为了解决类似上面的问题,就设计了接口这个概念。

接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么

再看定义:

接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

1.1.3 接口的定义

type 接口类型名 interface{
        方法名1( 参数列表1 ) 返回值列表1
        方法名2( 参数列表2 ) 返回值列表2}
1.接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,
如,有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
2.方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
3.参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

type writer interface{
    Write([]byte) error
}

1.1.4. 实现接口的条件

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表

啥意思?一个程序还必须把接口中的方法用完?

我们来定义一个Sayer接口:

package main

import "fmt"

type Sayer interface {
	say()
}

type dog struct {
}
type cat struct {
}

// 因为Sayer接口里只有一个say方法,所以我们只需要给dog和cat 分别实现say方法就可以实现Sayer接口了。
// Dog实现say方法
func (d dog) say() {
	fmt.Println("汪汪汪")
}

// cat实现say方法
func (c cat) say() {
	fmt.Println("喵喵喵")
}

// 接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dog和cat类型的变量。
func main() {
	var x Sayer // 声明一个Sayer类型的变量x
	a := cat{}  // 实例化一个cat
	b := dog{}  // 实例化一个dog
    // 还是得赋值啊,让接口统一管理
    
	x = a       // 可以把cat实例直接赋值给x
	x.say()     // 喵喵喵
	x = b       // 可以把dog实例直接赋值给x
	x.say()     // 汪汪汪
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值