在golang的开发中,我们经常会用到类型断言 type assertions 和 switch x.(type) 类型选择,他们都可以对interface{} 空接口类型的数据进行类型断言, 他们的功能类似但是有区别, 区别如下:
共同点: 都可以对interface{} / any 类型的数据进行数据类型的断言
区别:
类型断言 x.(T) 只能对 interface{} / any 类型的数据进行数据类型的断言
switch x.(type)类型选择 可以对 interface{} / any 类型数据进行选择, 也可以对接口类型数据进行类型选择(这个是他和类型断言x.(T) 的主要区别)
他们的详细用法和使用示例见golang白皮书详解, 为了保留原汁原味,直接贴官方英文了,单词也简单,应该都能看明白!
Type assertions类型断言官方详解
For an expression x of interface type, but not a type parameter, and a type T, the primary expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.
More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.
If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program.
var x interface{} = 7 // x has dynamic type int and value 7
i := x.(int) // i has type int and value 7
type I interface { m() }
func f(y I) {
s := y.(string) // illegal: string does not implement I (missing method m)
r := y.(io.Reader) // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
…
}
A type assertion used in an assignment statement or initialization of the special form
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.
Type switches 类型选择官方详解
A type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a type assertion using the keyword type rather than an actual type:
switch x.(type) {
// cases
}
Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different.
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .
The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.
Instead of a type, a case may use the predeclared identifier nil; that case is selected when the expression in the TypeSwitchGuard is a nil interface value. There may be at most one nil case.
Given an expression x of type interface{}, the following type switch:
switch i := x.(type) {
case nil:
printString("x is nil") // type of i is type of x (interface{})
case int:
printInt(i) // type of i is int
case float64:
printFloat64(i) // type of i is float64
case func(int) float64:
printFunction(i) // type of i is func(int) float64
case bool, string:
printString("type is bool or string") // type of i is type of x (interface{})
default:
printString("don't know the type") // type of i is type of x (interface{})
}
could be rewritten:
v := x // x is evaluated exactly once
if v == nil {
i := v // type of i is type of x (interface{})
printString("x is nil")
} else if i, isInt := v.(int); isInt {
printInt(i) // type of i is int
} else if i, isFloat64 := v.(float64); isFloat64 {
printFloat64(i) // type of i is float64
} else if i, isFunc := v.(func(int) float64); isFunc {
printFunction(i) // type of i is func(int) float64
} else {
_, isBool := v.(bool)
_, isString := v.(string)
if isBool || isString {
i := v // type of i is type of x (interface{})
printString("type is bool or string")
} else {
i := v // type of i is type of x (interface{})
printString("don't know the type")
}
}
A type parameter or a generic type may be used as a type in a case. If upon instantiation that type turns out to duplicate another entry in the switch, the first matching case is chosen.
func f[P any](x any) int {
switch x.(type) {
case P:
return 0
case string:
return 1
case []P:
return 2
case []byte:
return 3
default:
return 4
}
}
var v1 = f[string]("foo") // v1 == 0
var v2 = f[byte]([]byte{}) // v2 == 2
The type switch guard may be preceded by a simple statement, which executes before the guard is evaluated.
The "fallthrough" statement is not permitted in a type switch.