检查值是不是实现接口的说明
Posted
技术标签:
【中文标题】检查值是不是实现接口的说明【英文标题】:Explanation of checking if value implements interface检查值是否实现接口的说明 【发布时间】:2015-03-04 09:57:23 【问题描述】:我已经阅读了“Effective Go”和其他类似的问答:golang interface compliance compile type check,但我仍然无法正确理解如何使用这种技术。
请看示例:
type Somether interface
Method() bool
type MyType string
func (mt MyType) Method2() bool
return true
func main()
val := MyType("hello")
//here I want to get bool if my value implements Somether
_, ok := val.(Somether)
//but val must be interface, hm..what if I want explicit type?
//yes, here is another method:
var _ Iface = (*MyType)(nil)
//but it throws compile error
//it would be great if someone explain the notation above, looks weird
如果实现了接口,有没有简单的方法(例如不使用反射)检查值?
【问题讨论】:
怎么样 _, ok := interface(val).(Somether) ? 【参考方案1】:我有一个解决方案可以用来补充恐慌处理程序模式 我没有对代码进行详尽的测试,但随意测试是肯定的 我欢迎任何改进建议或其他 Go 专家的东西
// -------------------------------------------------------------- //
// hasErrIface -
// ---------------------------------------------------------------//
func hasErrIface(v reflect.Value) (error, bool)
// CanInterface reports whether Interface can be used without panicking
if !v.CanInterface()
return nil, false
// Interface panics if the Value was obtained by accessing unexported struct fields
err, ok := v.Interface().(error)
return err, ok
// -------------------------------------------------------------- //
// HasErrKind
// ---------------------------------------------------------------//
func HasErrKind(r interface) (err error, isErr bool)
err = nil
isErr = false
v := reflect.ValueOf(r)
switch v.Kind()
case reflect.Struct:
errtype := reflect.TypeOf((*error)(nil)).Elem()
if v.Type().Implements(errtype)
err, isErr = v.Interface().(error)
case reflect.Ptr:
err, isErr = hasErrIface(v)
case reflect.Interface:
err, isErr = hasErrIface(v)
return
// -------------------------------------------------------------- //
// EvalErrKind
// ---------------------------------------------------------------//
func EvalErrKind(r interface) (errval error)
err, isErr := HasErrKind(r)
if !isErr
errtxt := "Unknown system error - %v :\n%v"
v := reflect.ValueOf(r)
return fmt.Errorf(errtxt, v.Type(), v)
return err
// -------------------------------------------------------------- //
// PanicHandler
// ---------------------------------------------------------------//
func PanicHandler(errHandler func(error)) func()
return func()
var err error
if r := recover(); r != nil
switch r.(type)
case ApiError:
err = r.(ApiError)
default:
err = EvalErrKind(r)
if errHandler != nil
errHandler(err)
【讨论】:
【参考方案2】:你也可以采用Alpha的解决方案:
val := MyType("hello")
var i interface = val
v, ok := i.(Somether)
...并进一步减少它:
val := MyType("hello")
v, ok := interface(val).(Somether)
如果您尝试测试一次性方法,您甚至可以执行以下操作:
val := MyType("hello")
v, ok := interface(val).(interface
Method() bool
)
注意:请确保您对“指针接收器”与“值接收器”实现非常小心。如果实现使用指针,则在传入值对象时断言将失败。如果实现使用值接收器,则两个断言都会通过。
// Implement Somether as a POINTER receiver method
func (mt *MyType) Method() bool
return true
func main()
val := MyType("hello")
v, ok := interface(val).(Somether)
fmt.Println(v, ok)
// Output: <nil> false
// Notice the pass by reference
v, ok := interface(&val).(Somether)
fmt.Println(v, ok)
// Output: 0xc000010200 true
对
// Implement Somether as a VALUE receiver method
func (mt MyType) Method() bool
return true
func main()
val := MyType("hello")
v, ok := interface(val).(Somether)
fmt.Println(v, ok)
// Output: hello true
// Notice the pass by reference
v, ok := interface(&val).(Somether)
fmt.Println(v, ok)
// Output: 0xc00008e1e0 true
【讨论】:
【参考方案3】:也可以通过以下方式使用reflect.Type
的Implements(u Type) bool
方法:
package main
import (
"reflect"
)
type Somether interface
Method() bool
type MyType string
func (mt MyType) Method() bool
return true
func main()
inter := reflect.TypeOf((*Somether)(nil)).Elem()
if reflect.TypeOf(MyType("")).Implements(inter)
print("implements")
else
print("doesn't")
您可以在documentation 中阅读更多信息。
【讨论】:
这是一个规范的例子,很好地记录在文档中。但是,@Alpha 解决方案要漂亮得多【参考方案4】:以下将起作用:
val:=MyType("hello")
var i interface=val
v, ok:=i.(Somether)
【讨论】:
【参考方案5】:如果你不知道值的类型,你只需要检查一个值是否实现了一个接口。 如果类型已知,则编译器会自动进行检查。
如果你真的想检查,你可以用你给的第二种方法来做:
var _ Somether = (*MyType)(nil)
编译时会出错:
prog.go:23: cannot use (*MyType)(nil) (type *MyType) as type Somether in assignment:
*MyType does not implement Somether (missing Method method)
[process exited with non-zero status]
您在这里所做的是将MyType
类型的指针(和nil
值)分配给Somether
类型的变量,但由于变量名称为_
,因此它被忽略了。
如果MyType
实现了Somether
,它将编译并且什么都不做
【讨论】:
由于右手有一个指向MyType
的指针,为什么黑色标识符不一定必须是*Somether
?我还在学习。 :-)
你可以把接口值想象成一个容器,你可以在里面放任何你想要的东西,只要它实现了正确的方法。它可以包含指向结构的指针,也可以直接包含结构。根据经验,您永远不需要创建指向接口值的指针以上是关于检查值是不是实现接口的说明的主要内容,如果未能解决你的问题,请参考以下文章