6.2. 实现接口的条件

6.2.1. 1.条件一:接口的方法与实现接口的类型方法格式一致

package main

import (
    "fmt"
)

// 定义一个数据写入器
type DataWriter interface {
    WriteData(data interface{}) error
}

// 定义结构体,用于实现DataWriter
type file struct {
}

// 实现DataWriter接口的WriteData()方法
func (d *file) WriteData(data interface{}) error {
    // 模拟数据写入
    fmt.Println("WriteData:", data)
    return nil
}
func main() {
    // 实例化file
    f := new(file)
    // 声明一个DataWriter的接口
    var writer DataWriter

    // 将接口赋值给f,也就是*file类型
    writer = f
    // 使用DataWriter接口进行数据写入
    writer.WriteData("data")        //WriteData: data

}

6.2.2. 2.条件二: 接口中所有方法均被实现

当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。

// 定义一个数据写入器
type DataWriter1 interface {
    WriteData(data interface{}) error

    // 能否写入
    CanWrite() bool
}

新增了CanWrite()方法,返回bool,此时编译就会报错.

注意:接口中的方法必须要全部实现,才能实现接口。

6.2.3. 3.一个接口的示例

package main

import "fmt"

type People interface {
    Speak(string) string
}

type Student struct {}

func (stu *Student) Speak(think string) (talk string) {
    if think == "sb" {
        talk = "你是个大帅比"
    }else{
        talk = "您好"
    }
    return talk
}

func main() {
    // 实例化一个结构体
    p :=new(Student)
    // 声明接口
    var p1 People
    //把接口赋值给结构体p
    p1 = p
    think := "bitch"
    fmt.Println(p1.Speak(think))    //您好

    think1 :="sb"
    fmt.Println(p1.Speak(think1))   //你是个大帅比
}

6.2.4. 4.面向对象的三大特性

  1. 继承

  2. 封装

  3. 多态

4.1多态和继承

现在了解了接口和方法集背后的机制,最后来看一个展示接口的多态行为的例子

package main

import (
    "fmt"
)

// 创建一个notifier的接口
type notifier interface {
    notify()
}

// user在程序里定义一个用户类型
type user struct {
    name  string
    email string
}

// admin定义了程序里的管理员
type admin struct {
    // 这里使用了继承,子类继承父类!!
    user
    level  string
    istrue bool
}

// notify是使用指针接收者实现的方法,实现了接口方法
func (u *user) notify() {
    fmt.Printf("Sending user email to %s<%s>\n", u.name, u.email)
}

// notify是使用指针接收者实现的方法,,实现了接口方法
func (a *admin) notify() {
    fmt.Printf("Sending administrator email to %s<%s> level:%s login:%t\n",
        a.name, a.email, a.level, a.istrue)
}

// 多态函数,传入接口根据接口实现相对于的方法
func sendNotification(n notifier) {
    n.notify()
}

func main() {
    // 创建一个user类型的值,并发送通知
    name := user{
        name:  "hujianli",
        email: "123@qq.com",
    }
    //多态函数sendNotification
    sendNotification(&name)
    fmt.Println("------------------------------------------------------------")
    admin1 := admin{
        user:   user{"hujian", "1234@qq.com"},
        level:  "super",
        istrue: true,
    }
    //多态函数sendNotification
    sendNotification(&admin1)
    fmt.Println("-----------------------------------------------------------")

    /**
    如果外部类型实现了notify 方法,内部类型的实现就不会被提升。
    不过内部类型的值一直存在,因此还可以通过直接访问内部类型的值,来调用没有被提升的内部类型实现的方法。

    在面对对象编程语言中,这叫类的重写,
    虽然重写覆盖了子类的notify,但是也是可以在实例化下的结构体方法中将它找出来
     */

    // 我们可以直接访问内部类型的方法
    admin1.user.notify()
    // 内部类型的方法也被提升到外部类型
    admin1.notify()
}

/**
------------------------------------------------------------
Sending administrator email to hujian<1234@qq.com> level:super login:true
-----------------------------------------------------------
Sending user email to hujian<1234@qq.com>
Sending administrator email to hujian<1234@qq.com> level:super login:true
 */

因为sendNotification 接受notifier 类型的接口值,所以这个函数可以同时执行useradmin 实现的行为。

4.2封装

有时候,你可能不希望公开包里的某个类型、函数或者方法这样的标识符。在这种情况,需要一种方法,将这些标识符声明为包外不可见,这时需要将这些标识符声明为未公开的。

D:.
└─src
    ├─counters
    │      counters.go
    │
    └─listing64
            listing64.go

counters.go

package counters

// 这个类型用于保存告警计数,未公开的私有类型结构体
type alertCounter int
// 保存一个字符串
type name string

// 保存一个结构体
type student struct {
    Name string
    Sex bool
    Age int
    // 注意此处是使用的结构体属性
    email string
}

// 此私有的函数无法被外部包所调用
func provide(s string) string {
    return s
}

// 首字母大写后,变成公开的对象
func New_int(value int) alertCounter {
    return alertCounter(value)
}

// 首字母大写后,变成公开的对象
func New_str(value string) name {
    return name(value)
}

listing64.go

package main

import (
    "fmt"
    "github.com/go_study/day08/src/counters"
)

func main() {
    new_int := counters.New_int(10000)
    new_str := counters.New_str("this is test string")
    fmt.Printf("counters_int is %d\n", new_int)
    fmt.Printf("counters_str is %s\n", new_str)

    // 此处可以看到email的属性无法获取,被隐藏了 一个名为email 的未公开的字段
    student1 := counters.Student{
        Name: "hu",
        Sex:  true,
        Age:  19,
    }
    fmt.Printf("Student name:%s sex:%v age:%d", student1.Name, student1.Sex, student1.Age)
}

/**
counters_int is 10000
counters_str is this is test string
Student name:hu sex:true age:19
 */

总结:

  • 标识符要么是从包里公开的,要么是在包里未公开的。

  • 通过大写首字母进行公开,小写首字母就是未公开。