验证一个接口是否满足另一个接口

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

Verifying that one interface satisfies another

问题

type Roller interface {
Min() int
}

type minS struct {}
func (m minS) Min() int {return 0}
func (m minS) Max() int {return 0}

func RollerSpec(c gospec.Context) {

var r Roller = minS{}

c.Specify("has a minimum value.", func() {
    _, ok := r.(interface{Min() int})
    c.Expect(ok, Equals, true)
})

c.Specify("has a maximum value.", func() {
    _, ok := r.(interface{Max() int})
    c.Expect(ok, Equals, true)
})

c.Specify("has an expected value.", func() {
    _, ok := r.(interface{Exp() int})
    c.Expect(ok, Equals, true)
})

c.Specify("can be rolled.", func() {
    _, ok := r.(interface{Roll() int})
    c.Expect(ok, Equals, true)
})

}

英文:

I'm writing a specification for a toy package I'm writing, and while most of the spec is guaranteeing that various structs in the package satisfy the primary public interface, I am also interested in specifying the methods that the interface must require; I know this is more than a little pedantic, but I figured it would be a neat experiment, and would apply pressure to keep the public interface stable.

This is my first try:

type Roller interface {
        Min()  int
}

type minS struct {}
func (m minS) Min() int {return 0}
func (m minS) Max() int {return 0}

func RollerSpec(c gospec.Context) {

        var r Roller = minS{}

        c.Specify("has a minimum value.", func() {
                _, ok := r.(interface{Min() int})
                c.Expect(ok, Equals, true)
        })

        c.Specify("has a maximum value.", func() {
                _, ok := r.(interface{Max() int})
                c.Expect(ok, Equals, true)
        })
        
        c.Specify("has an expected value.", func() {
                _, ok := r.(interface{Exp() int})
                c.Expect(ok, Equals, true)
        })

        c.Specify("can be rolled.", func() {
                _, ok := r.(interface{Roll() int})
                c.Expect(ok, Equals, true)
        })
}

As you can see, my Roller interface only requires Min() but minS implements both Min() and Max(). I pass the first two specs, even though Runner doesn't satisfy interface{Max() int} because the dummy type I use to test it does. Likewise, declaring r without a base type causes it to fail all specs.

It is obvious why Go would have type assertion between interfaces work on the actual stored type, but it's not what I'm looking for here. I've also checked the reflect package, but it seems like it only inspects structs as well. Is there a way to programmatically check whether an interface requires a given method without pulling in the parse package myself and crawling the parse tree for the method name?

答案1

得分: 2

简单来说,你不能这样做。因为接口不是具体类型,所以无法直接存储接口。反射只能用于具体类型。请参考reflect包及其文档。

无论如何,你想做的事情似乎是不必要的。接口本身就是你的规范。你似乎想要编写一个描述规范的规范,一旦你这样做了,最重要的问题是你何时停止。这是一个无限循环的过程。

英文:

Simply put you can't. There is no way to store just an interface since it's not a concrete type and reflection only works on concrete types. See the reflect package and it's documentation there.

What you want to do seems unnecessary anyway. The interface is your spec. What it looks like you want to do is write a spec to describe your spec and once you do that the big question is where do you stop. It's turtles all the way down 验证一个接口是否满足另一个接口

答案2

得分: 1

使用单独的接口进行简单类型断言就可以了。

type minRoller interface {
    Min() int
}

type maxRoller interface {
    Max() int
}

在你的测试中:

if _, ok := r.(minRoller); ok {
    print("有最小值")
}
if _, ok := r.(maxRoller); ok {
    print("有最大值") 
}

你的Roller接口可以嵌入较小的接口:

type MinRoller interface {
    minRoller
}

type MinMaxRoller interface {
    minRoller
    maxRoller
}
英文:

Simple type assertions with individual interfaces should do it.

type minRoller interface {
    Min() int
}

type maxRoller interface {
    Max() int
}

And in your test:

if _, ok := r.(minRoller); ok {
    print("has a min value")
}
if _, ok := r.(maxRoller); ok {
    print("has a max value") 
}

Your Roller interface can embed the smaller ones:

type MinRoller interface {
    minRoller
}

type MinMaxRoller interface {
    minRoller
    maxRoller
}

答案3

得分: 0

当然有一种方法 - 接口的reflect.Type包含与接口方法对应的reflect.Method(不要尝试调用这些方法!请参阅我的问题Go reflection with interface embedded in struct - how to detect “real” functions?)。

使用这个方法,通过循环遍历接口的方法并检查这些方法是否也存在于另一个接口中,几乎可以轻松实现:

func InterfaceExtendsInterface(parent reflect.Type, child reflect.Type) bool {
    if parent.Kind() != reflect.Interface || child.Kind() != reflect.Interface {
        return false
    }
    for i := 0; i < parent.NumMethod(); i++ {
        m := parent.Method(i)
        if n, has := child.MethodByName(m.Name); !has {
            return false
        } else if !reflect.DeepEqual(m,n) {
            return false
        }
    }
    return true
}

由于接口“扩展”(接口嵌入在接口中)的方式,我实际上看不出以下两种情况之间的区别:

type Foo interface {...}
type Bar interface { Foo; ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem())

和稍微弱一点的情况:

type Foo interface {...}
type Bar interface { ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem()) &&
                        // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Foo{}).Elem())

我甚至无法想象任何情况下这两种情况会对Baz实例的非反射调用产生不同的语义...(当然,除非需要直接转换为Foo或不需要转换,如果Bar没有嵌入它)。

英文:

Of course there is a way - the reflect.Type of an interface contains reflect.Method which correspond to the interface methods (don't try to call these, though! See my question Go reflection with interface embedded in struct - how to detect “real” functions?)

Using this, it's (almost) trivial to loop through the methods of an interface and check that those methods also exist in another interface:

func InterfaceExtendsInterface(parent reflect.Type, child reflect.Type) bool {
    if parent.Kind() != reflect.Interface || child.Kind() != reflect.Interface {
	    return false
    }
    for i := 0; i &lt; parent.NumMethod(); i++ {
	    m := parent.Method(i)
	    if n, has := child.MethodByName(m.Name); !has {
		    return false
	    } else if !reflect.DeepEqual(m,n) {
		    return false
	    }
    }
    return true
}

Because of the way interface "extension" (embedding of interface in interface) works, I don't really see a difference between

type Foo interface {...}
type Bar interface { Foo; ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem())

and the slightly weaker

type Foo interface {...}
type Bar interface { ... }
type Baz struct { ... } // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Bar{}).Elem()) &amp;&amp; 
                        // reflect.TypeOf(Baz{}).CovertibleTo(reflect.TypeOf([]Foo{}).Elem())

to the point where I can't even think of any case where the two would ever imply different semantics for non-reflective calls on instances of Baz... (other than having to convert to Foo or not directly if Bar does not embed it, of course)

huangapple
  • 本文由 发表于 2012年8月30日 11:24:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/12189432.html
匿名

发表评论

匿名网友

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

确定