Golang methods with same name and arity, but diffe

2019-04-18 05:32发布

问题:

The following code works fine. Two methods operating on two different structs and printing a field of the struct:

type A struct {
  Name string
}

type B struct {
  Name string
}

func (a *A) Print() {
  fmt.Println(a.Name)
}

func (b *B) Print() {
  fmt.Println(b.Name)
}

func main() {

  a := &A{"A"}
  b := &B{"B"}

  a.Print()
  b.Print()
}

Shows the desired output in the console:

A
B

Now, if I change the method signature in the following way I get an compile error. I just move the receiver of the method to the arguments of the method:

func Print(a *A) {
  fmt.Println(a.Name)
}

func Print(b *B) {
  fmt.Println(b.Name)
}

func main() {

  a := &A{"A"}
  b := &B{"B"}

  Print(a)
  Print(b)
}

I can't even compile the program:

./test.go:22: Print redeclared in this block
    previous declaration at ./test.go:18
./test.go:40: cannot use a (type *A) as type *B in function argument

Question: Why is it, that I can interchange struct types in the receiver, but not in the arguments, when the methods have the same name and arity?

回答1:

Because Go does not support overloading of user-defined functions on their argument types.

You can make functions with different names instead, or use methods if you want to "overload" on only one parameter (the receiver).



回答2:

You can use type introspection. As a general rule, though, any use of the generic interface{} type should be avoided, unless you are writing a large generic framework.

That said, a couple of ways to skin the proverbial cat:

Both methods assume a Print() method is defined for both types (*A and *B)

Method 1:

func Print(any interface{}) {
    switch v := any.(type) {
    case *A:
        v.Print()
    case *B:
        v.Print()
    default:
        fmt.Printf("Print() invoked with unsupported type: '%T' (expected *A or *B)\n", any)
        return
    }
}

Method 2:

type Printer interface {
    Print()
}

func Print(any interface{}) {
    // does the passed value honor the 'Printer' interface
    if v, ok := any.(Printer); ok {
        // yes - so Print()!
        v.Print()
    } else {
        fmt.Printf("value of type %T passed has no Print() method.\n", any)
        return
    }
}

If it's undesirable to have a Print() method for each type, define targeted PrintA(*A) and PrintB(*B) functions and alter Method 1 like so:

    case *A:
        PrintA(v)
    case *B:
        PrintB(v)

Working playground example here.