接口
原创大约 5 分钟
“鸭子”接口
package main
import "fmt"
// go这种动态语言认为属性不重要,但方法很重要,只要实现了接口
// 那它就是鸭子类型
// 定义鸭子接口
type Duck interface {
// 存放方法的声明
swimming()
flying()
walking()
}
// 定义鸭子结构体
type DuckInstance struct {
legs int
weight int
}
// 鸭子结构体实现鸭子接口
// 即使不用指针类型的参数,也能调用
func (di *DuckInstance) swimming() {
fmt.Println("swimming like a duck")
}
func (di *DuckInstance) flying() {
fmt.Println("flying like a duck")
}
func (di *DuckInstance) walking() {
fmt.Println("walking like a duck")
}
func main() {
// 鸭子类型:go语言中处处都是interface,所以到处都是鸭子类型
/*
当看到一只鸟,走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就是鸭子
也就是说,某个类是不是“鸭子”,要看它的方法能不能像鸭子一样工作
go语言认为属性不重要,方法才是区分结构体是否是接口的关键
鸭子类型强调的是事物的外部行为特征,而不是内部的结构属性
*/
// var di Duck = &DuckInstance{}
// 虽然上面这种声明方式也可以,但还是养成用new声明的习惯
// 而且即使不用指针类型的参数,也能调用,也就是说
/*
var di Duck = DuckInstance{}
di.swimming()
di.flying()
di.walking()
这样也是能够调用成功的
但是,swimming()、flying()和walking()方法必须都实现,才能用“子类”DuckInstance实例化
*/
// 这里 new(DuckInstance) 等价于 &DuckInstance{},但推荐使用 new(DuckInstance) 方式
var di Duck = new(DuckInstance)
di.swimming()
di.flying()
di.walking()
}
实现多个接口
package main
import "fmt"
type CustomerWriter interface {
write() error
}
type CustomerReader interface {
read() error
}
// 将接口放在结构体中,成为一个变量
type ContentOperator struct {
// 匿名变量
CustomerWriter
CustomerReader
}
// 定义一个实现文件读写的结构体
type File struct {
path string
}
// 定义一个实现数据库读写的结构体
type DataBase struct {
host string
port int
url string
}
func (fw *File) write() error {
fmt.Println("write content to file")
return nil
}
func (dbw *File) read() error {
fmt.Println("read content from file")
return nil
}
func (fw *DataBase) write() error {
fmt.Println("write content to database")
return nil
}
func (dbw *DataBase) read() error {
fmt.Println("read content from database")
return nil
}
//func (co *ContentOperator) write() error {
// fmt.Println("write")
// return nil
//}
//func (co *ContentOperator) read() error {
// fmt.Println("read")
// return nil
//}
func main() {
/*
实现多个接口
*/
// 写入
var writer CustomerWriter = &ContentOperator{
// 这里只需要调用不同的实现,就能完成不同的功能
// 如果这里用命名变量,就必须显式实现 CustomerWriter 接口
&DataBase{},
nil,
}
_ = writer.write()
// 读取
var reader CustomerReader = &ContentOperator{
nil,
// 指定从哪里读取
&File{},
}
_ = reader.read()
}
动态类型传参
package main
import (
"fmt"
"strings"
)
/*
有若干种add方法,除了参数不同,其他完全一样
*/
func add(a, b int) int {
return a + b
}
func add32(a, b int32) int32 {
return a + b
}
func add64(a, b int64) int64 {
return a + b
}
// 将类型参数和返回值都定义成interface{}
func adder(a, b interface{}) interface{} {
// 所谓断言,就是类型判断
// a.(type)类似于java的instanceof
switch a.(type) {
case int:
return a.(int) + b.(int)
case int32:
return a.(int32) + b.(int32)
case int64:
return a.(int64) + b.(int64)
case string:
return a.(string) + b.(string)
}
return nil
}
/*
通过接口解决动态类型传参的问题
*/
func main() {
a := 1
b := 2
//fmt.Println(add(a, b))
//fmt.Println(add32(int32(a), int32(b)))
//fmt.Println(add64(int64(a), int64(b)))
fmt.Println(adder(a, b))
fmt.Println(adder(int32(a), int32(b)))
fmt.Println(adder(int64(a), int64(b)))
result := adder("hello", " world")
fmt.Println(result)
// 但result并不是真正的字符串类型,因为它是int类型,不能做一些常规的字符串操作
// 如果想要用,必须做断言
res, _ := result.(string)
fmt.Println(res)
// 遍历切片
for _, v := range strings.Split(res, " ") {
fmt.Println(v)
}
}
接口嵌套
package main
import "fmt"
type Outer interface {
write(string)
}
type Inner interface {
read(string)
}
// MyInterface 嵌套的接口
type MyInterface interface {
Outer
Inner
test()
}
type FileOperator struct {
name string
}
// 实现嵌套接口中的方法
func (f *FileOperator) write(s string) {
//TODO implement me
fmt.Println("write")
}
func (f *FileOperator) read(s string) {
//TODO implement me
fmt.Println("read")
}
func (f *FileOperator) test() {
//TODO implement me
fmt.Println("test")
}
/*
接口嵌套
*/
func main() {
var fo MyInterface = new(FileOperator)
fo.write("hello")
fo.read("world")
fo.test()
}
接口中的slice
package main
import "fmt"
func mPrint(data ...interface{}) {
for _, value := range data {
fmt.Println(value)
}
}
func mPrint2(data interface{}) {
fmt.Println(data)
}
/*
使用slice时的常见错误
*/
func main() {
//// 下面这样打印是可以的
//var data = []interface{}{
// "bear", 1, 1.85, true,
//}
//mPrint(data...)
//// 但如果尝试这样打印,会报错
//var data = []string{
// "bear", "xiangwang",
//}
//mPrint(data...)
// 它必须这样才行
var data = []string{
"bear", "xiangwang",
}
var data2 []interface{}
for _, value := range data {
data2 = append(data2, value)
}
mPrint(data2...)
}
接口注入
package main
import (
"fmt"
"strings"
)
// 连接,有可能是数据库连接,也可能是网络连接
type Connection interface {
connect(...string)
}
// 数据库连接
type DatabaseConnection struct {
host string
port string
user string
password string
dbname string
}
// 网络连接
type NetworkConnection struct {
url string
}
// 定义数据库连接,实现connect()方法
func (dbc *DatabaseConnection) connect(args ...string) {
builder := strings.Builder{}
builder.WriteString("DatabaseConnection connect config: ")
for i := 0; i < len(args); i++ {
builder.WriteString(args[i])
builder.WriteString(", ")
}
fmt.Println(builder.String())
}
// 定义网络连接,实现connect()方法
func (nc *NetworkConnection) connect(args ...string) {
builder := strings.Builder{}
builder.WriteString("NetworkConnection connect URL: ")
for i := 0; i < len(args); i++ {
builder.WriteString(args[i])
builder.WriteString(", ")
}
fmt.Println(builder.String())
}
// 定义要被注入的结构体
type Injection struct {
Connection // 匿名注入接口
}
/*
接口注入
*/
func main() {
// 给连接注入
var connection1 Connection = &Injection{
&DatabaseConnection{},
}
connection1.connect("127.0.0.1", "3306", "root", "123456", "system")
var connection2 Connection = &Injection{
&NetworkConnection{},
}
connection2.connect("www.baidu.com")
}
感谢支持
更多内容,请移步《超级个体》。