Explanation of checking if value implements interface

huangapple go评论69阅读模式
英文:

Explanation of checking if value implements interface

问题

我已经阅读了《Effective Go》和其他类似的问答,比如这个:https://stackoverflow.com/questions/17994519/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")

    // 这里我想要得到一个布尔值,判断我的值是否实现了Somether接口
    _, ok := val.(Somether)
    // 但是val必须是一个接口,嗯...如果我想要明确的类型呢?

    // 是的,这里有另一种方法:
    var _ Iface = (*MyType)(nil)
    // 但是它会抛出编译错误
    // 如果有人能解释一下上面的表示法,那就太好了,看起来很奇怪
}

有没有简单的方法(例如不使用反射)来检查一个值是否实现了一个接口?

英文:

I've read "Effective Go" and other Q&As like this: https://stackoverflow.com/questions/17994519/golang-interface-compliance-compile-type-check , but nonetheless I can't understand properly how to use this technique.

Please, see example:

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
}

Is there any simple ways (eg without using reflection) check value if it implements an interface?

答案1

得分: 86

只有在不知道值的类型时,才需要检查一个值是否实现了一个接口。
如果类型已知,编译器会自动进行这个检查。

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

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,它将编译并且不会有任何操作。

英文:

You only have to check if a value implements an interface if you don't know the value's type.
If the type is known, that check is automatically done by the compiler.

If you really want to check anyways, you can do it with the second method you gave:

var _ Somether = (*MyType)(nil)

which would error at compile time:

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]

What you are doing here, is assigning a pointer of MyType type (and nil value) to a variable of type Somether, but since the variable name is _ it is disregarded.

If MyType implemented Somether, it would compile and do nothing

答案2

得分: 24

以下是翻译好的内容:

以下代码将起作用:

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

Following will work:

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

答案3

得分: 17

以下是翻译好的内容:

也可以使用reflect.TypeImplements(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")
	}
}

您可以在文档中阅读更多相关信息。

英文:

It is also possible to use Implements(u Type) bool method of reflect.Type in the following way:

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")
	}
}

You can read more on that in the documentation.

答案4

得分: 13

你还可以采用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
}) 

注意:在实现中非常小心“指针接收器”与“值接收器”。如果实现使用指针,当传递值对象时,断言将失败。如果实现使用值接收器,两个断言都将通过。

// 将Somether实现为指针接收器方法
func (mt *MyType) Method() bool {
  return true
}

func main() {
    val := MyType("hello")

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // 输出:  <nil> false

    // 注意通过引用传递
    v, ok := interface{}(&val).(Somether)
    fmt.Println(v, ok)
    // 输出:  0xc000010200 true

}

// 将Somether实现为值接收器方法
func (mt MyType) Method() bool {
  return true
}

func main() {
    val := MyType("hello")

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // 输出:  hello true

    // 注意通过引用传递
    v, ok := interface{}(&val).(Somether)
    fmt.Println(v, ok)
    // 输出:  0xc00008e1e0 true

}
英文:

You can also take Alpha's solution:

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

... and reduce it further:

val := MyType(&quot;hello&quot;)
v, ok := interface{}(val).(Somether)

If you're trying to test for one-off methods, you can even do something like:

val := MyType(&quot;hello&quot;)
v, ok := interface{}(val).(interface {
	Method() bool
}) 

>NOTE: Make sure you are very careful with "pointer receiver" versus "value receiver" implementations. If the implementation uses a pointer the assertion will fail when passing in a value object. If the implementation uses a value receiver, both assertions will pass.

// Implement Somether as a POINTER receiver method
func (mt *MyType) Method() bool {
  return true
}

func main() {
    val := MyType(&quot;hello&quot;)

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // Output:  &lt;nil&gt; false

    // Notice the pass by reference
    v, ok := interface{}(&amp;val).(Somether)
    fmt.Println(v, ok)
    // Output:  0xc000010200 true

}

versus

// Implement Somether as a VALUE receiver method
func (mt MyType) Method() bool {
  return true
}

func main() {
    val := MyType(&quot;hello&quot;)

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // Output:  hello true

    // Notice the pass by reference
    v, ok := interface{}(&amp;val).(Somether)
    fmt.Println(v, ok)
    // Output:  0xc00008e1e0 true

}

答案5

得分: 3

我有一个解决方案,用来补充恐慌处理程序模式。
我还没有对代码进行详尽的测试,但是经过简单的测试是肯定的。
我欢迎任何改进或其他Go专家的建议。

// -------------------------------------------------------------- //
// hasErrIface -
// ---------------------------------------------------------------//
func hasErrIface(v reflect.Value) (error, bool) {
	// CanInterface报告是否可以在不恐慌的情况下使用Interface
	if !v.CanInterface() {
		return nil, false
	}
	// 如果通过访问未导出的结构字段获得了Value,则Interface会引发恐慌
	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 := "未知系统错误 - %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)
			}
		}
	}
}
英文:

I have a solution that I use to complement a panic handler pattern<br>
I have not exhaustively tested the code but casual testing is affirmative<br>
I welcome any suggestions to improve or other go expert stuff

// -------------------------------------------------------------- //
// 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 := &quot;Unknown system error - %v :\n%v&quot;
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)
}
}
}
}

答案6

得分: 1

只能在Go 1.18或更高版本中使用

// 实现接口
func implInter[T any](obj any) bool {
    _, ok := obj.(T)
    return ok
}

var obj any = "some text"

if implInter[string](obj) {
    newObj, _ := obj.(string)
} 
英文:

It only works with Go 1.18 or higher

//implements interface
func implInter[T any](obj any) bool {
⠀ _, ok := obj.(T)
return ok
}
var obj any = &quot;some text&quot;
if impleInter[string](obj) {
⠀ newObj, _ := obj.(string)
} 

答案7

得分: 0

你也可以使用以下代码:

如果MyType实现了Somether接口,这段代码应该可以编译通过:

// 使用new关键字创建指向MyType类型的指针
var _ Somether = new(MyType)

var _ Somether = MyType("")

请注意,这里的MyTypeSomether是占位符,你需要将其替换为实际的类型和接口名称。

英文:

You can also use this:

If MyType implements the Somether interface, this should compile

// use new to create a pointer to type MyType
var _ Somether = new(MyType) 
var _ Somether = MyType(&quot;&quot;)

huangapple
  • 本文由 发表于 2015年1月7日 01:09:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/27803654.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定