golang 编译时检查类型是否实现了接口

97 阅读2分钟

这行代码:

var _ business.Handler = (*QueryList)(nil)

作用

var _ business.Handler = new(QueryList) 类似,它的主要作用是在编译时检查 *QueryList 类型是否实现了 business.Handler 接口。其具体作用是:

  1. 类型安全检查
    如果 *QueryList 没有实现 business.Handler 中声明的所有方法,编译器会抛出错误。

  2. 声明用途
    这是一个惯用法,表明 QueryList 或其指针类型 (*QueryList) 被设计为实现 business.Handler 接口。这是 Go 社区中常用的实践,尤其是在大型项目中,用于文档化和防止错误。


为什么使用 (*QueryList)(nil)

这里 (nil) 是一个零值指针,只是为了表示类型,而不是实际分配任何内存。

  • (*QueryList)(nil) 是一个指向 QueryList 的空指针,它没有分配实际的 QueryList 实例。
  • 编译器会检查 *QueryList 是否满足 business.Handler 接口的约束。

运行时和内存开销

这行代码不会对运行时或内存产生任何开销,原因如下:

  1. 编译时行为
    Go 编译器在编译时完成类型检查,不会生成对应的运行时代码,也不会分配任何内存。

  2. nil 指针不会分配内存
    (nil) 表示空指针,既不初始化结构体,也不分配任何实际内存空间。


为什么更推荐 (*QueryList)(nil) 而不是 new(QueryList)

虽然两者都能实现编译时检查,但有一些细微区别:

方式优点
var _ business.Handler = (*QueryList)(nil)语义明确,仅用于类型检查,不会分配实例,更简洁,惯用法。
var _ business.Handler = new(QueryList)编译时检查有效,但 new(QueryList) 明显看起来像是要实际创建一个实例(尽管它并未执行),容易引起误解。

因此,社区更倾向于使用 (*QueryList)(nil)


总结

var _ business.Handler = (*QueryList)(nil)
  • 作用:在编译时检查 *QueryList 是否实现了 business.Handler 接口;
  • 开销:无运行时或内存开销;
  • 惯例:推荐用 (*QueryList)(nil),因为它更明确表达了“仅进行类型检查”的意图。