首先golang是强类型的语言,如果类型不匹配的话编译就通不过,所以一个对象是否实现了某个接口根本就不需要判断,看这个的对象的类型就知道了。
之所以会有这样的问题,是因为一个对象的类型可能是未知的,具体表现就是它是一个接口,要么是空接口 interface{}, 要么是非空接口 interfaceA。
判断一个接口对象是什么类型使用接口的类型断言。
func f(v interface{}) {
if _, ok := v.(int64); ok {
fmt.Println("int64")
}
}
或者使用 switch 。
同样的,接口也是一种类型,也可以通过断言的方式来判断一个对象是否实现了某个接口。
func f(v interface{}) {
if _, ok := v.(InterfaceA); ok {
fmt.Println("InterfaceA")
}
}
还有一个场景就是,因为一个对象可能同时实现了多个接口,由于某种约束,同一个对象在不同的地方只能表现出一种特性,此时也需要断言。
func tt(v InterfaceA) {
// do some thing as InterfaceA
// do some thing as InterfaceB
if _, ok := v.(InterfaceB); ok {
fmt.Println("ok")
}
}
在阅读 database/sql 源码的时候看到这样的使用场景。
func Register(name string, driver driver.Driver) {
driversMu.Lock()
defer driversMu.Unlock()
if driver == nil {
panic("sql: Register driver is nil")
}
if _, dup := drivers[name]; dup {
panic("sql: Register called twice for driver " + name)
}
drivers[name] = driver
}
type Driver interface {
Open(name string) (Conn, error)
}
func Open(driverName, dataSourceName string) (*DB, error) {
driversMu.RLock()
driveri, ok := drivers[driverName]
driversMu.RUnlock()
if !ok {
return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
if driverCtx, ok := driveri.(driver.DriverContext); ok {
connector, err := driverCtx.OpenConnector(dataSourceName)
if err != nil {
return nil, err
}
return OpenDB(connector), nil
}
return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil
}
type DriverContext interface {
OpenConnector(name string) (Connector, error)
}
作为一个驱动,github.com/go-sql-driver/mysql 调用 init 方法注册到 database/sql 中供其使用。
后者并不知道前者是否实现了某些接口,在使用之前需要做断言。