如果类型T1
和T2
基于类型T
,则类型T
仅来自NewT1()
或NewT2()
,函数func (*T) WhoAmI()
是否可以知道“真的”是T1
还是T2
?
package main import "fmt" import "reflect" type T struct { s string } func (v *T) WhoAmI() string { // pull type name with reflect fmt.Println( reflect.TypeOf(v).Elem().Name() ) // always prints "T"! // todo: if I am actually T1 return "T1" // todo: else if I am actually T2 return "T2" } type T1 T func NewT1( s string ) T1 { return T1{ s } } type T2 T func NewT2( s string ) T2 { return T2{ s } } func main() { var t1 = T1{ "xyz" } var t2 = T2{ "pdq" } s1 := ((*T)(&t1)).WhoAmI() // would like to return "T1" s2 := ((*T)(&t2)).WhoAmI() // would like to return "T2" fmt.Println( s1, s2 ) }
从技术上讲:
一旦t1
类型T1
被强制转换为T
类型,因此可以调用func (*T) WhoAmI()
,t1
是否完全失去了其类型真正T1
的事实{1}}?如果没有,我们如何从接收类型T
的方法的角度回收知识?
一般来说:
换句话说,如果一个类型基于另一个类型,如果派生类型的变量被强制转换为基类型以运行方法,那么该方法是否可以学习调用它的接收者的真实类型?
答案 0 :(得分:8)
不,这是不可能的。从旧类创建新类型与创建从基于类的语言中的父类继承的新类不同。在你的情况下,T对T1或T2一无所知,如果你正在调用WhoAmI方法,你可以根据定义使用类型为T的接收器。
您的设计可能会更好地使用界面。尝试更像这样的东西:
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" }
上试试
T1和T2都实现了接口T,因此它们可以用作类型T.
答案 1 :(得分:4)
Evan的回答很好。但是,有多种方法可以解决这个问题,更接近您的目标。
当你转换时,你实际上改变了类型,没有任何残余。 Go只关心当前类型是什么。
解决这个问题的一种方法就是编写一个函数。函数对于共享实现非常有用。一些面向对象的语言将它们抛弃为不纯净,但是它们不知道它们缺少什么(我正在看着你的公共静态虚空!)。
func WhoAmI(v interface{}) string {
switch v.(type) {
case *T: return "*T"
case *T1: return "*T1"
case *T2: return "*T2"
}
return "unknown"
}
现在您不必转换值以调用方法/函数。当然,如果您要进行类型切换并为每种类型执行不同的操作,您也可以为每种类型编写不同的方法。
要使其成为一种方法,您可以这样做:
type T struct { s string }
func (t *T) WhoAmI() string { return WhoAmI(t) }
type T1 T
func (t1 *T1) WhoAmI() string { return WhoAmI(t1) }
这样,您无需重新实现该方法。
如果你真的希望T
知道自己,那就自己动手吧!有两种方法可以做到这一点。一个是作为参数:
func (t *T) WhoAmI(self interface{}) string { ... }
...
fmt.Println(t.WhoAmI(t))
fmt.Println(((*T)(t1)).WhoAmI(t1))
这样做的好处是你不需要做任何额外的工作。该方法可以同时访问t和self,因此它具有两全其美的优点。但是,这会成为界面的一部分,这有点尴尬。
你也可以把它变成一个字段:
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
}
现在,T
或T1
上的所有方法都可以通过检查self
来判断最初创建对象的内容。
你可以来回转换来获取方法,或者你可以使用一个名为embedding的功能:
type T struct{}
func (t *T) M() {}
type T1 struct { T }
...
var t T
var t1 T1
t.M()
t1.M()
如您所见,您可以通过T.M
或t
致电t1
。不过请注意,T.M
始终只会看到T
,无论您将其称为t
还是t1
。您必须使用上述策略之一才能让T.M
能够看到T1
。