请教一个 golang 接口方法设计的问题

2021-08-14 21:25:32 +08:00
 zzyphp111

背景 :通过断言是怎么实现接口实例化注入

疑问的关键代码:

var _ log.Logger = (*stdLogger)(nil)

提出疑问:

首先有这样一个结构,表示 log 结构器


// Logger is a logger interface.
type Logger interface {
	Log(level Level, keyvals ...interface{}) error
}

接下来按照正规流程,就是利用这个 logger 器,做些初始化 log 的方法


var _ Logger = (*stdLogger)(nil)

type stdLogger struct {
	log  *log.Logger
	pool *sync.Pool
}

// NewStdLogger new a logger with writer.
func NewStdLogger(w io.Writer) Logger {
	return &stdLogger{
		log: log.New(w, "", 0),
		pool: &sync.Pool{
			New: func() interface{} {
				return new(bytes.Buffer)
			},
		},
	}
}

问题就在这里为什么,明明这个 new 方法返回的是 stdLogger 这个结构,而为啥返回类型是 Logger,我发现个特别点就是第一行做了断言处理,这个是什么原理,一般断言也是把已知结构转成明确固定结构,为啥这里明明 stdLogger 结构体和 Logger 结构体完全不一样,就可以实现断言?

望大家不吝赐教,多多指出,或相关代码实现,本人对接口实现方面比较小白。

1981 次点击
所在节点    问与答
15 条回复
johnwood
2021-08-14 21:34:52 +08:00
golang 的类型系统和 java 之类的不一样,他是鸭子类型系统,结构不用明确表明自己实现了某个接口,也没有 java 那样的“继承”。go 的倾向是组合优于继承。

回到问题,估计因为 stdLogger 里有嵌入的 log *log.Logger
zzyphp111
2021-08-14 21:40:22 +08:00
zzyphp111
2021-08-14 22:42:33 +08:00
@johnwood #1 这也是我所以疑问的,你看 zap 的引入就没有嵌入 log *log.Logger,但也断言成功了,这是为什么 https://github.com/go-kratos/kratos/blob/main/examples/log/zap.go
gjquoiai
2021-08-14 22:57:21 +08:00
一个编译时检查某个东西是否实现了某个接口的技巧,现在用的不多了,都用 linter 了
johnwood
2021-08-14 23:23:56 +08:00
@zzyphp111 你看#3 连接的 16 行 有嵌入自己的实现的 logger
comwrg
2021-08-14 23:25:06 +08:00
4L 说的对,是用来编译期保证正确的实现了某个接口,而不是等着运行之后程序 panic
EscYezi
2021-08-15 00:05:03 +08:00
只要实现了 Logger 所有的方法就实现了 Logger 接口
楼主提到的 stdLogger 和 zapLogger 可以断言成功是因为两个结构体都实现了 Logger 接口的 Log 方法,和内嵌的那个 log 成员没关系
johnwood
2021-08-15 00:12:45 +08:00
@EscYezi 你看 NewStdLogger 实例化的是什么
EscYezi
2021-08-15 00:18:07 +08:00
@johnwood #8 实例化的 stdLogger 啊,stdLogger 实现了 Logger 接口
EscYezi
2021-08-15 00:52:54 +08:00
可以尝试运行一下这段代码


package main

import "fmt"

type Logger interface {
Log(...interface{})
}
type myLogger struct {
}

func (l *myLogger) Log(...interface{}) {
fmt.Println("my logger")
}
func main() {
var l Logger = (*myLogger)(nil)
l.Log("aaa")
}
johnwood
2021-08-15 10:44:37 +08:00
@EscYezi 看了 2 楼的连接 你说的对 stdLogger 实现了 Logger 接口
zzyphp111
2021-08-15 12:14:50 +08:00
@EscYezi #10 嗯嗯,看了你的,感觉非常直观,也解决了我的疑问, 只要存在这个结构体的实现 就可以断言成功。

感谢
kksco
2021-08-16 13:53:49 +08:00
zzyphp111
2021-08-16 14:26:42 +08:00
@kksco #13 非常感谢
Ansen
2022-11-16 17:26:35 +08:00
我也在这里迷糊了,搜索到这帖子了

然后学习群的群友推荐了这篇文章,看完茅塞顿开

https://go-kratos.dev/en/blog/

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/795816

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX