funcpersonJob(p string) { if p == "student" { println("student's task is study") } elseif p == "worker" || p == "civil servant" || p == "server" { println("Serving for people are their's task") } elseif p == "Boss" || p == "Leader" { println("Serving for stuff are their's task") } elseif p == "teacher" { println("Teaching children are their's task") } else { println("uhh...") } }
用switch改写上面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
funcpersonJob(p string) { switch p { case"student": println("student's task is study") case"worker", "civil servant", "server": println("Serving for people are their's task") case"Boss", "Leader": println("Serving for stuff are their's task") case"teacher": println("Teaching children are their's task") default: println("uhh...") } }
personJob("civil servant")
从代码呈现角度上看,switch多分支控制结构确实要比if分支控制结构要更见简洁美观。
接下来,分析下switch的一般形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
switch initStmt; expr { case expr1: // 执行分支1 case expr2: // 执行分支2 case expr3_1, expr3_2, expr3_3: // 执行分支3 case expr4: // 执行分支4 ... ... case exprN: // 执行分支N default: // 执行默认分支 }
funcpersonJob(p string) { switch a := p; a { case"student": println("student's task is study") case"worker", "civil servant", "server": println("Serving for people are their's task") case"Boss", "Leader": println("Serving for stuff are their's task") case"teacher": println("Teaching children are their's task") default: println("uhh...") } }
另外,还有一点要注意的是,如果某个 case 语句已经是 switch 语句中的最后一个 case 了,并且它的后面也没有 default 分支了,那么这个 case 中就不能再使用 fallthrough,否则编译器就会报错。
type switch
Go 语言的 switch 语句还支持求值结果为类型信息的表达式,也就是 type switch 语句。我们来看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funcmain() { var x interface{} = 13 switch x.(type) { casenil: println("x is nil") caseint: println("the type of x is int") casestring: println("the type of x is string") casebool: println("the type of x is string") default: println("don't support the type") } }
>> the type of x is int
switch 关键字后面跟着的表达式为x.(type),这种表达式形式是 switch 语句专有的,而且也只能在 switch 语句中使用。这个表达式中的 x 必须是一个接口类型变量,表达式的求值结果是这个接口类型变量对应的动态类型。
着,case 关键字后面接的就不是普通意义上的表达式了,而是一个个具体的类型。这样,Go 就能使用变量 x 的动态类型与各个 case 中的类型进行匹配,之后的逻辑就都是一样的了。
通过x.(type),我们除了可以获得变量 x 的动态类型信息之外,也能获得其动态类型对应的值信息,现在我们把上面的例子改造一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funcmain() { var x interface{} = 13 switch v := x.(type) { casenil: println("v is nil") caseint: println("the type of v is int, v =", v) casestring: println("the type of v is string, v =", v) casebool: println("the type of v is bool, v =", v) default: println("don't support the type") } }
>> the type of v is int, v = 13
这里我们将 switch 后面的表达式由x.(type)换成了v := x.(type)。对于后者,你千万不要认为变量 v 存储的是类型信息,其实 v 存储的是变量 x 的动态类型对应的值信息,这样我们在接下来的 case 执行路径中就可以使用变量 v 中的值信息了。
另外,在前面的 type switch 演示示例中,我们一直使用 interface{}这种接口类型的变量,Go 中所有类型都实现了 interface{}类型,所以 case 后面可以是任意类型信息。
但如果在 switch 后面使用了某个特定的接口类型 I,那么 case 后面就只能使用实现了接口类型 I 的类型了,否则 Go 编译器会报错。可以看看这个例子:
funcmain() { var t T var i I = t switch i.(type) { case T: println("it is type T") caseint: println("it is type int") casestring: println("it is type string") } }
在这个例子中,我们在 type switch 中使用了自定义的接口类型 I。那么,理论上所有 case 后面的类型都只能是实现了接口 I 的类型。但在这段代码中,只有类型 T 实现了接口类型 I,Go 原生类型 int 与 string 都没有实现接口 I,于是在编译上述代码时,编译器会报出如下错误信息:
1 2 3
19:2: impossible type switch case: i (type I) cannot have dynamic type int (missing M method) 21:2: impossible type switch case: i (type I) cannot have dynamic type string (missing M method)
switch在循环中使用
我们来实现一个代码,找出整型切片中的第一个偶数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
funcmain() { var sl = []int{5, 19, 6, 3, 8, 12} var firstEven int = -1
for i := 0; i < len(sl); i++ { switch sl[i] % 2 { case0: firstEven = sl[i] break case1: // do nothing } } println(firstEven) }