- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
最近开始学习Go语言。
我试图理解 Go 中的接口(interface)原理,但被一件事完全迷惑了。
鸭子原则说:如果某物叫声像鸭子,走路像鸭子,那么它就是鸭子。
但我想知道如果我们有三个这样的接口(interface),Go 会如何表现:
// Interface A
type InterfaceA interface {
ActionA() string
}
// Interface B
type InterfaceB interface {
ActionB() string
}
和接口(interface)C
,它做一些不同的事情,但具有类似于接口(interface)A
和B
功能的功能:
// Interface C with methods A and B interfaces
type InterfaceC interface {
ActionA() string
ActionB() string
}
然后我们有三个实现上述接口(interface)的结构体:
type StructA struct{}
// If it does ActionA then it's interface A
func (a StructA) ActionA() string {
return "Interface A implementation"
}
type StructB struct{}
// If it does ActionB then it's interface B
func (b StructB) ActionB() string {
return "Interface B implementation"
}
type StructC struct{}
// If it does ActionA and ActionB, it's an Interface C
func (c StructC) ActionA() string {
return "Interface C implementation"
}
func (c StructC) ActionB() string {
return "Interface C implementation"
}
还有一个函数来标识它获取的类型:
func getType(data interface{}) string {
switch data.(type) {
default:
return "Unknown"
case InterfaceA:
return "Interface A"
case InterfaceB:
return "Interface B"
case InterfaceC:
return "Interface C"
}
}
main
函数中的代码:
func main() {
a := StructA{}
fmt.Println(a.ActionA())
fmt.Println(getType(a)) // should return InterfaceA
fmt.Println("")
b := StructB{}
fmt.Println(b.ActionB())
fmt.Println(getType(b)) // should return InterfaceB
fmt.Println("")
c := StructC{}
fmt.Println(c.ActionA())
fmt.Println(c.ActionB())
fmt.Println(getType(c)) // should return InterfaceC
}
输出:
Interface A implementation
Interface A
Interface B implementation
Interface B
Interface C implementation
Interface C implementation
Interface A
经过一些实验,我发现如果我们更改 switch
中的 case
顺序,那么该函数会正确识别类型:
func getType(data interface{}) string {
switch data.(type) {
default:
return "Unknown"
case InterfaceC:
return "Interface C"
case InterfaceB:
return "Interface B"
case InterfaceA:
return "Interface A"
}
}
输出:
Interface A implementation
Interface A
Interface B implementation
Interface B
Interface C implementation
Interface C implementation
Interface C
另请参阅 play.golang.org 上的完整代码
我的问题:这是错误还是功能?如果它是一项功能,我应该如何更改 getType
以使该功能不依赖于 case
顺序?
最佳答案
这是预期的工作方式,由语言规范定义。
switch
有两种类型声明,Expression switches和 Type switches ,并且此行为记录在表达式开关中:
In an expression switch, the switch expression is evaluated and the case expressions, which need not be constants, are evaluated left-to-right and top-to-bottom; the first one that equals the switch expression triggers execution of the statements of the associated case; the other cases are skipped. If no case matches and there is a "default" case, its statements are executed. There can be at most one default case and it may appear anywhere in the "switch" statement.
[...]
A type switch compares types rather than values. It is otherwise similar to an expression switch.
在 Go 中,一个类型隐式实现了一个接口(interface),如果它是method set。是接口(interface)的超集。没有意向声明。所以在 Go 中,哪个接口(interface)定义了方法并不重要,唯一重要的是方法签名:如果一个类型具有接口(interface)“规定”的所有方法,那么该类型隐式实现了所述接口(interface)。
问题是您想要将 Type 开关用于它不是为它设计的目的。你想找到仍然由值实现的“最广泛”类型(具有最多方法)。仅当您按此预期顺序枚举案例(不同类型)时,它才会执行此操作。
话虽如此,在您的情况下,不存在值仅 InterfaceC
实现这样的事情。您的代码不会说谎:实现 InterfaceC
的所有值也将实现 InterfaceA
和 InterfaceB
,因为 的方法集InterfaceA
和InterfaceB
是InterfaceC
方法集的子集。
如果你想能够“区分”InterfaceC
实现,你必须“改变”方法集,这样上面提到的关系就不会成立(InterfaceC< 的方法集
不会是 InterfaceA
和 InterfaceB
方法集的超集)。如果您希望 StructC
不是 InterfaceA
实现,则必须更改 ActionA()
的方法签名(在 InterfaceA
或在 InterfaceC
中),并且类似地 ActionB()
不是 InterfaceB
实现。
您还可以向 InterfaceA
(和 InterfaceB
)添加 InterfaceC
中缺少的方法:
type InterfaceA interface {
ActionA() string
implementsA()
}
type InteraceB interface {
ActionB() string
implementsB()
}
当然,你必须将它们添加到 StructA
和 StructB
中:
func (a StructA) implementsA() {}
func (b StructB) implementsB() {}
这样您就可以获得所需的输出。在 Go Playground 上试用.
如果您不能或不想这样做,您唯一的选择是以正确的顺序列举案例。或者不要为此使用类型开关。
关于go - 代码行为取决于 switch 运算符中的类型顺序,如何摆脱它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43136897/
Or 运算符 对两个表达式进行逻辑“或”运算。 result = expression1 Or expression2 参数 result 任意数值变量。 expression1 任意
Not 运算符 对表达式执行逻辑非运算。 result = Not expression 参数 result 任意数值变量。 expression 任意表达式。 说明 下表显示如何
Is 运算符 比较两个对象引用变量。 result = object1 Is object2 参数 result 任意数值变量。 object1 任意对象名。 object2 任意
\ 运算符 两个数相除并返回以整数形式表示的结果。 result = number1\number2 参数 result 任意数值变量。 number1 任意数值表达式。 numbe
And 运算符 对两个表达式进行逻辑“与”运算。 result = expression1 And expression2 参数 result 任意数值变量。 expression1
运算符(+) 计算两个数之和。 result = expression1 + expression2 参数 result 任意数值变量。 expression1 任意表达式。 exp
我对此感到困惑snippet : var n1 = 5-"4"; var n2 = 5+"4"; alert(n1); alert(n2); 我知道 n1 是 1。那是因为减号运算符会将字符串“4”转
我想我会得到 12,而不是 7。 w++,那么w就是4,也就是100,而w++, w 将是 8,1000;所以 w++|z++ 将是 100|1000 = 1100 将是 12。 我怎么了? int
Xor 运算符 对两个表达式进行逻辑“异或”运算。 result = expression1 Xor expression2 参数 result 任意数值变量。 expression1
Mod 运算符 两个数值相除并返回其余数。 result = number1 Mod number2 参数 result 任意数值变量。 number1 任意数值表达式。 numbe
Imp 运算符 对两个表达式进行逻辑蕴涵运算。 result = expression1 Imp expression2 参数 result 任意数值变量。 expression1 任
Eqv 运算符 执行两个表达式的逻辑等价运算。 result = expression1 Eqv expression2 参数 result 任意数值变量。 expression1 任
我有一个运算符重载的简单数学 vector 类。我想为我的运算符(operator)获取一些计时结果。我可以通过计时以下代码轻松计时我的 +=、-=、*= 和/=: Vector sum; for(s
我是用户定义比较运算符的新手。我正在读一本书,其中提到了以下示例: struct P { int x, y; bool operator、运算符<等),我们
在 SQL 的维基百科页面上,有一些关于 SQL 中 bool 逻辑的真值表。 [1] 维基百科页面似乎来源于 SQL:2003 标准。 等号运算符 (=) 的真值表与 SQL:2003 草案中的 I
我遇到了一个奇怪的 C++ 运算符。 http://www.terralib.org/html/v410/classoracle_1_1occi_1_1_number.html#a0f2780081f
我正在阅读关于 SO 和 answers 中的一个问题,它被提到为: If no unambiguous matching deallocation function can be found, pr
我偶然发现了这个解决方案,但我无法理解其中到底发生了什么。谁能解释一下! 据我了解,它试图通过计算一半的单元格然后将其加倍来计算 a*b 网格中的单元格数量。但是我无法理解递归调用。 请不要建议其他解
Go的基本类型 布尔类型bool 长度:1字节 取值:布尔类型的取值只能是true或者false,不能用数字来表示 整型 通用整型 int / uint(有符号 / 无符号,下面也类似) 长度:根据运
在本教程中,您将学习JavaScript中可用的不同运算符,以及在示例的帮助下如何使用它们。 什么是运算符? 在JavaScript中,运算符是一种特殊符号,用于对运算数(值和变量)执行操作。例如,
我是一名优秀的程序员,十分优秀!