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.面向对象的三大特性¶
继承
封装
多态
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
类型的接口值,所以这个函数可以同时执行user 和admin
实现的行为。
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
*/
总结:
标识符要么是从包里公开的,要么是在包里未公开的。
通过大写首字母进行公开,小写首字母就是未公开。