One type is coerced into another, can a method to determine the type of the receiver?

No, it's not possible. Creating a new type from an old one is not like creating a new class that inherits from a parent class in an class-based language. In your case T knows nothing about either T1 or T2 and if you're calling the WhoAmI method you have a receiver of type T by definition.

Your design might work better with an interface. Try something more like this:

type T interface {
    WhoAmI() string
}

type T1 struct {
    s string
}

func (t *T1) WhoAmI() string { return "T1" }

type T2 struct {
    s string
}

func (t *T2) WhoAmI() string { return "T2" }

Try it on the Go playground

T1 and T2 both implement the interface T, so they can be used as type T.


Evan's answer is good. However, there are multiple ways to solve this problem that are closer to what you were looking for.

When you convert, you actually change types, there's nothing residual. Go only cares what the current type is.

One approach to get around this is just to write a function. Functions are very useful for having shared implementation. Some object oriented languages threw them out as being impure, but they don't know what they're missing (I'm looking at you public static void!).

func WhoAmI(v interface{}) string {
    switch v.(type) {
    case *T: return "*T"
    case *T1: return "*T1"
    case *T2: return "*T2"
    }

    return "unknown"
}

Now you don't have to convert the value in order to call the method/function. Of course, if you're going to do a type switch and do something different for each type, you might as well just write a different method for each type.

To make it a method, you could do:

type T struct { s string }
func (t *T) WhoAmI() string { return WhoAmI(t) }

type T1 T
func (t1 *T1) WhoAmI() string { return WhoAmI(t1) }

This way, you don't need to reimplement the method.

If you really want T to know itself, give it a self! There are two ways of doing this. One is as a parameter:

func (t *T) WhoAmI(self interface{}) string { ... }
...
fmt.Println(t.WhoAmI(t))
fmt.Println(((*T)(t1)).WhoAmI(t1))

The upside of this is that you don't need to do any extra work. The method has access to both t and self, so it has the best of both worlds. However, this becomes part of your interface, which is a little awkward.

You could also make it a field:

type T struct { self interface{} }
func NewT() *T {
    t := new(T)
    t.self = t
    return t
}

type T1 T
func NewT1() *T1 {
    t1 := new(T1)
    t1.self = t1
    return t1
}

Now, any methods on a T or T1 can tell what the object was originally created as by checking self.

You can keep converting back and forth to get methods, or you can use a feature called embedding:

type T struct{}
func (t *T) M() {}

type T1 struct { T }
...
var t T
var t1 T1
t.M()
t1.M()

As you can see, you can call T.M via t or t1. However, keep in mind that T.M will always only see a T, regardless of what you call it on (t or t1). You'll have to use one of the above strategies in order for T.M to be able to see a T1.

Tags:

Types

Go