检查value是否实现接口的说明

时间:2015-01-06 17:09:28

标签: interface go type-conversion

我已阅读" Effective Go"和其他Q&像这样: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
}

是否有任何简单的方法(例如,不使用反射)检查值是否实现了接口?

5 个答案:

答案 0 :(得分:26)

如果您不知道值的类型,则只需检查值是否实现了接口。 如果类型已知,则该检查由编译器自动完成。

如果您真的想要检查,可以使用您提供的第二种方法:

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的变量,但由于变量名称为{{1}它被忽视了。

如果_已实施MyType,则会编译并不执行任何操作

答案 1 :(得分:6)

您还可以采用Alpha的解决方案:

EXTRA_STREAM

...并进一步减少:

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

}

答案 2 :(得分:3)

还可以通过以下方式使用Implements(u Type) bool的{​​{1}}方法:

reflect.Type

您可以在documentation中阅读更多内容。

答案 3 :(得分:2)

以下将起作用:

val:=MyType("hello")
var i interface{}=val
v, ok:=i.(Somether)

答案 4 :(得分:0)

我有一个解决方案,用于补充恐慌处理程序模式
我没有对代码进行详尽的测试,但随意测试是肯定的
我欢迎任何改进建议或其他 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)
            }
        }
    }
}