Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if struct implements a given interface

Tags:

reflection

go

I need to walk through all the field of a struct type and check if they implements a given interface.

type Model interface {...}

func HasModels(m Model) {
    s := reflect.ValueOf(m).Elem()
    t := s.Type()
    modelType := reflect.TypeOf((*Model)(nil)).Elem()

    for i := 0; i < s.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf("%d: %s %s -> %s\n", i, f.Name, f.Type, f.Type.Implements(modelType)) 
    }       
}

Then, if a call HasModels with a struct like so :

type Company struct {...}

type User struct {
    ...
    Company Company
}

HasModels(&User{})

With Company and User both implementing Model; I get f.Type.Implements(ModelType) returning false for the Company field of the User struct.

This is unexpected, so, what Am I doing wrong here ?

like image 573
arnaud briche Avatar asked Sep 02 '13 09:09

arnaud briche


People also ask

How do you know if a class implements an interface?

To declare a class that implements an interface, you include an implements clause in the class declaration. Your class can implement more than one interface, so the implements keyword is followed by a comma-separated list of the interfaces implemented by the class.

How do you implement an interface in Golang?

Implementing an interface in Go To implement an interface, you just need to implement all the methods declared in the interface. Unlike other languages like Java, you don't need to explicitly specify that a type implements an interface using something like an implements keyword.

How do interfaces work in Golang?

Go language interfaces are different from other languages. In Go language, the interface is a custom type that is used to specify a set of one or more method signatures and the interface is abstract, so you are not allowed to create an instance of the interface.

What is Type assertion in Golang?

Type assertions in Golang provide access to the exact type of variable of an interface. If already the data type is present in the interface, then it will retrieve the actual data type value held by the interface. A type assertion takes an interface value and extracts from it a value of the specified explicit type.


3 Answers

You've unfortunately left out the essential parts (please always post complete programs), so I can only guess that the problem is in a method defined on a pointer receiver, in which case the behavior of your code is expected. Check this example and its output:

package main

import (
        "fmt"
        "reflect"
)

type Model interface {
        m()
}

func HasModels(m Model) {
        s := reflect.ValueOf(m).Elem()
        t := s.Type()
        modelType := reflect.TypeOf((*Model)(nil)).Elem()

        for i := 0; i < s.NumField(); i++ {
                f := t.Field(i)
                fmt.Printf("%d: %s %s -> %t\n", i, f.Name, f.Type, f.Type.Implements(modelType))
        }
}

type Company struct{}

func (Company) m() {}

type Department struct{}

func (*Department) m() {}

type User struct {
        CompanyA    Company
        CompanyB    *Company
        DepartmentA Department
        DepartmentB *Department
}

func (User) m() {}

func main() {
        HasModels(&User{})
}

Playground


Output:

0: CompanyA main.Company -> true
1: CompanyB *main.Company -> true
2: DepartmentA main.Department -> false
3: DepartmentB *main.Department -> true
like image 138
zzzz Avatar answered Oct 05 '22 11:10

zzzz


There's an easier way of doing this that doesn't need reflect. For example:

type middlewarer interface {Middleware() negroni.Handler}
for _, controller := range ctrls {
    if m, ok := interface{}(controller).(middlewarer); ok {
        n.Use(m.Middleware())
    }
}

calls the Middleware() method only in those slice elements that implement the middlewarer interface.

like image 29
David Tootill Avatar answered Oct 05 '22 12:10

David Tootill


If the variable is not an interface, make it one.

foo := interface{}(yourVar)

Then you can check if it implements the interface you're interested in.

bar, ok := foo.(yourInterface)
if !ok {
    // handle the case where it doesn't match the interface
}
bar.methodOnYourInterface()

Here's a playground link for a more fleshed out example

like image 37
andygarfield Avatar answered Oct 05 '22 10:10

andygarfield