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

时间:2012-08-30 03:24:58

标签: reflection interface go bdd

我正在为我正在编写的玩具包编写规范,虽然大多数规范都保证包中的各种结构满足主公共接口,但我也有兴趣指定接口必须的方法要求;我知道这不仅仅是一个小小的迂腐,但我认为这将是一个很好的实验,并会施加压力以保持公共界面稳定。

这是我的第一次尝试:

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

如您所见,我的Roller界面只需要Min(),但minS同时实现了Min()Max()。我传递了前两个规范,即使Runner不满足interface{Max() int},因为我用它来测试它的虚拟类型。同样地,声明没有基类型的r会导致它失败所有规格。

很明显为什么Go会在接口之间使用类型断言处理实际存储的类型,但这不是我在这里寻找的。我还检查了反射包,但似乎它只检查结构。有没有办法以编程方式检查接口是否需要给定的方法而不自己拉入解析包并抓取解析树以获取方法名称?

3 个答案:

答案 0 :(得分:2)

简单地说你不能。没有办法只存储一个接口,因为它不是具体类型,反射只适用于具体类型。请参阅反射包及其文档。

无论如何,你想做什么似乎都是不必要的。界面是你的规格。你想要做的是写一个规范来描述你的规范,一旦你这样做,最重要的问题是你在哪里停止。它一直是乌龟: - )

答案 1 :(得分:1)

具有单独接口的简单类型断言应该这样做。

type minRoller interface {
    Min() int
}

type maxRoller interface {
    Max() int
}

在你的测试中:

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

您的Roller界面可以嵌入较小的界面:

type MinRoller interface {
    minRoller
}

type MinMaxRoller interface {
    minRoller
    maxRoller
}

答案 2 :(得分:0)

当然有一种方法 - 界面的reflect.Type包含reflect.Method,它们对应于界面方法(不要尝试调用这些!请参阅我的问题{{3 }})

使用它,它(几乎)微不足道地遍历接口的方法并检查这些方法是否也存在于另一个接口中:

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
}

由于界面的方式&#34;扩展&#34; (在界面中嵌入界面)有效,我真的看不出

之间的区别
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的实例的非反射调用意味着不同语义的任何情况......(除了必须转换为{ {1}}或者当Foo没有嵌入它时直接{1}}