Go 基础语法 轻松上手 goland~

一个GO代码,hello,world

说到学习语言,那必然少不了我们的hello world了,先来看个简单的hello world代码

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
fmt.Println("hello, world")  
}
  1. 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
  2. 下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。

是不是感觉有点 java + c 的味道?笑

学过 java 的应该都知道println是输出并换行,这里好像无非就是把system.out换成了fmt,并且需要导入fmt这个包

然后又综合了python,句末不需要写分号, 并且if elsefor后不需要加括号

但是这里需要注意的是,他这个括号还就必须这样打了,不能单独打一行

例如:


接下来是go的一些基础语法

Go标识符

Go的标识符和c一样,是由字母、数字、下划线组成,并且第一个字符必须是字母或下划线,而不能是数字。


GO关键字

break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

除了以上的关键字,go语言还有36个预定义标识符

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr

GO运算符

与c++基本一致,需要注意的就是go的++和--不能参与运算,所以++和--也是只能放在变量的后面


GO语言注释

和c++一样, //注释单行, /* .... */ 注释多行


GO的变量与常量声明

变量:var + name + type = value 或者 name := value

使用 := 的话go就会自动推导类型,所以两种方式实际上是一样的,所以要注意前者不能和后者混用

注意 : := 符号只允许在函数中使用,即只能在声明局部变量的时候使用,而 var 没有这个限制。

例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    var a int = 3  
    fmt.Println(a)  
    b := 4  
    fmt.Println(b)  
}

输出结果为:

go 复制代码
3
4

下面这样就是错误的

下面这样也是错误的

另外,还可以一次声明多个变量并且赋值

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    var a, b, c int = 3, 4 ,5  
    fmt.Println(a, b, c)  
}

常量:const + name + type = value

例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    const a int = 3  
    fmt.Println(a)  
}

常量之后不能进行值的改变,不然就会报错

同样也可以一次声明多个值

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    const a, b, c int = 3, 4, 5  
    fmt.Println(a, b, c)  
}

GO语言基本语句

if else

与c++类似,也支持嵌套。不同的是if后面的表达式不需要写括号,并且if后面一定要跟大括号,建议根据以下格式规范写。

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
} else {
  /* 在布尔表达式为 false 时执行 */
}

例如:

go 复制代码
func main() {  
    a := 100  
    if a >= 100 {  
    fmt.Println(">=100")  
    } else {  
    fmt.Println("<100")  
    }  
}

switch语句

也与c++类似,但是这里的case默认自带break,也就是在成功执行一个case之后就会跳出,c++是遇到一个符合条件的case之后会一直执行后面case的语句直到遇到break才会停止。

go 复制代码
switch var1 {
case value1:
    ...
case value2:
    ...
default:
    ...
}

例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    score := 'A'  
    switch score {  
    case 'A':  
        fmt.Println("太棒了")  
    case 'B':  
        fmt.Println("继续加油")  
    case 'C':  
        fmt.Println("要努力哦")  
    default:  
        fmt.Println("下次加油哦")  
    }     
}

结果为:

go 复制代码
太棒了

fallthrough

想要实现c++的效果,可以使用fallthrough ,其实c++也有这玩意儿就是了

使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。

例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    score := 'A'  
    switch score {  
    case 'A':  
        fmt.Println("太棒了")  
        fallthrough  
    case 'B':  
        fmt.Println("继续加油")  
        fallthrough  
    case 'C':  
        fmt.Println("要努力哦")  
        fallthrough  
    default:  
        fmt.Println("下次加油哦")  
    }  
}

结果为:

go 复制代码
太棒了
继续加油
要努力哦
下次加油哦

for循环

go语言中没有while,但是能用for循环实现while的效果,基本用法也与c++相似。

下面是一个简单的for循环:

go 复制代码
for i := 1; i <= 5; i++ {  
    fmt.Println(i)  
}

可以通过以下方式实现while的效果:

go 复制代码
func main() {  
    n := 5  
    for n > 0 {  
        n--  
        fmt.Println(n)  
    }  
}

可以这样实现无限循环:

go 复制代码
for {  
    n--  
    fmt.Println(n)  
}

要停止无限循环,可以在命令窗口按下ctrl-c

For-each range

这种格式的循环可以对字符串、数组、切片、map等进行迭代输出元素。

这里要注意,遍历的时候,字符串、数组、切片这些有序的要用两个值 ,第一个是下标,第二个才是value。无序的map也要用两个值,一个是key,一个是value。同样也是不需要的值不使用就行了, 不想用的值可以使用_代替

例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
  
    s := "may all the beauty blessed"  
    strs := [3]string{"A", "B", "C"}  
    slice := []int{1, 2, 3}  
    map1 := map[int]string{  
        0: "48",  
        1: "49",  
        2: "50",  
    }  
    for i, c := range s {  
        fmt.Print(i, string(c))  
        fmt.Print(" ")  
    }  
    fmt.Println()  
    for _, s := range strs {  
        fmt.Print(s)  
        fmt.Print(" ")  
    }  
    fmt.Println()  
    for i, num := range slice {  
        fmt.Print(i, num)  
        fmt.Print(" ")  
    }  
    fmt.Println()  
    for key, value := range map1 {  
        fmt.Print(key, value)  
        fmt.Print(" ")  
    }  
}

输出结果为:

go 复制代码
0m 1a 2y 3  4a 5l 6l 7  8t 9h 10e 11  12b 13e 14a 15u 16t 17y 18  19b 20l 21e 22s 23s 24e 25d
A B C
0 1 1 2 2 3
048 149 250
// 前面的0,1, 2...是下标啊

这里还发现了print,类型相同会分开,类型不同不会分开,qwq


GO语言数据类型

Go是一种强类型语言

总共有四大类

  • 基础类型
  • 聚合类型
  • 引用类型
  • 接口类型

基础类型

基础类型与c语言类似,

有分为int, uint, float, complex, string, bool

int:

uint:

uintptr与unsafe.Pointer

这个uintptr是啥呢?这个牵扯到了指针,详情看深度解密Go语言之unsafe - 知乎 (zhihu.com)

按我的理解就是:

  • go语言中是有c语言中的指针的机制的,但是他为了安全,有种种限制,go语言中的一般指针是不能进行数学运算的。

下面用具体样例进行解释:

先来看个简单的指针用法,可以完全类比c

go 复制代码
import "fmt"  
  
func main() {  
a := [5]int{1, 2, 3, 4, 5}  
p := &a[0]  
c := 3  
c++  
fmt.Println(*p)  
fmt.Println(c)  
}

输出结果为:

1
4

这里熟悉c指针的伙伴们应该都没问题,但是go的指针他不能进行运算,例如

可以发现这里报错了

那么我们有没有办法实现c语言中,也是我们想要的这种指针p++取到a[1]的值的效果呢?

那就得用到uintptr

go 复制代码
package main  
  
import (  
"fmt"  
"unsafe"  
)  
  
func main() {  
a := [5]int{1, 2, 3, 4, 5}  
d := &a[0]  
fmt.Println(*(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(d)) + 8)))  
}

这里涉及到了两类指针,和我们的uintptr类型

  1. *类型:普通指针类型,用于传递对象地址,不能进行指针运算。
  2. unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。
  3. uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。

unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。

我们这里的操作是先把d指针从*int类型转化成了unsafe.Pointer类型,但是unsafe.Pointer类型不能进行运算,我们再转化为uintptr进行运行,我这里是人为计算出了一个int值占8个字节(go语言,一个int我是64位电脑就是8个字节,刚开始以为4个算错懵逼了一会儿,哭😥),当然我们也可以使用unsafe.Sizeof算字节,改成以下即可:

go 复制代码
func main() {  
a := [5]int{1, 2, 3, 4, 5}  
d := &a[0]  
fmt.Println(*(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(d)) + unsafe.Sizeof(a[0]))))  
}

计算完毕之后再重新转化为unsafe.Pointer类型,然后再转为为*int类型, 再*运算取结果,这样我们就利用指针++取到了a[1]的值,当然这里是为了便于理解,用了如此丑陋的方式...

float:

这里需要注意的是浮点型没有float,而一定要选择float32或者float64

complex:

这里也要注意必须指定是complex64还是complex128

complex64 对应的是float32,是所有实部为float32,虚部为float32的集合

complex128 对应的是float64,是所有实部为float64,虚部为float64的集合

string:

与c++类似,可以直接相加运算

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
var s1 string = "qwq"  
var s2 string = "qaq"  
var s3 string = s1 + s2  
fmt.Println(s3)  
}

输出结果为:

go 复制代码
qwqqaq

bool:

go 复制代码
var flag bool = false

真为true

假为false

默认值

go 语言可以不对变量进行初始化,所有数据类型都有默认值。因此我们可以不用担心变量未初始化问题。

  • int 类型和uint类型的 0
  • float32float64 类型的 0.0
  • bool 类型的 false
  • string 类型的空字符串
  • complex类型的(0+0i)

rune类型

这里有个问题,就是go的基础类型中并没有char类型。但是go中有rune类型,这是int32的别称,可以当做是c中的char类型,使用了utf-8编码,数字和字母的编码和我们常见的ascii编码值是一样的。

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    c := 'A'  
    c1 := 'a'  
    c2 := '0'  
    var c3 rune  
    c3 = '1'  
    fmt.Println(c, c1, c2, c3)  
}

输出结果为:

go 复制代码
65 97 48 49

聚合类型

Go的聚合类型分为数组结构体

数组

go中的数组也与c++基本一致

数组的定义:var arrayName [size]dataType

其中,arrayName 是数组的名称,size 是数组的大小,dataType 是数组中元素的数据类型。

例如,以下定义了name为arraym, size为10, type为int的数组

var array [10]int

数组初始化

数组元素如果不进行初始化的话,默认为初始化为0

下面是两种初始化方法:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    // 第一种 
    var array1 = [5]int{1, 2, 3, 4, 5}  
    // 第二种
    array2 := [5]int{1, 2, 3, 4, 5}  
    fmt.Println(array1[1], array2[1])  
}

如果数组长度不确定,可以使用[...]代替数组的长度,编译器会根据元素个数自行推断数组的长度:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    // 第一种  
    var array1 = [...]int{1, 2, 3, 4, 5}  
    // 第二种  
    array2 := [...]int{1, 2, 3, 4, 5}  
    fmt.Println(array1[1], array2[1])  
}

如果设置了数组的长度,我们还可以通过 指定下标 来初始化元素, 没有指定的会默认初始化为0:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    // 第一种  
    var array1 = [5]int{0: 1, 1: 2}  
    // 第二种  
    array2 := [5]int{0: 1, 1: 2}  
    fmt.Println(array1[1], array2[4])  
}
数组的应用

与c++一致,可以按下标访问,修改元素

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    a1 := [5]int{1, 2, 3, 4, 5}  
    fmt.Println(a1[1])  
    a1[1] = 0  
    fmt.Println(a1[1])  
}

输出结果为:

go 复制代码
2
0
数组的遍历:

可以类似c++用下标遍历,也可以使用for-each遍历

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
  
    strs := [3]string{"qwq", "qaq", "=.="}  
    for i := 0; i < len(strs); i++ {  
        fmt.Println(strs[i])  
    }  
    for _, i := range strs {  
        fmt.Println(i)  
    }  
}

结构体

go中的结构体也与c++基本一致

结构体定义

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:

go 复制代码
type struct_variable_type struct {
   member definition
   member definition
   ...
   member definition
}

例如:

go 复制代码
type class struct {  
    members int  
    primaryTeacher string  
    major string  
    grade string  
    classNumber int  
}  
结构体赋值

可以使用结构体名.成员名 访问结构体成员赋值, 可以直接使用结构体名+{value1, value2, '''}赋值,也可以使用结构体名+{结构体成员名:value'''}赋值,没有赋值的会取默认值

例如:

go 复制代码
package main  
  
import "fmt"  
  
type class struct {  
    members int  
    primaryTeacher string  
    major string  
    grade string  
    classNumber int  
}  
  
func main() {  
    class1 := class{35, "Feixin", "软件工程", "大二", 1}  
    fmt.Println(class1)  
    fmt.Println(class1.major)  
    var class2 class = class{major: "大数据"}  
    fmt.Println(class2.members)  
    fmt.Println(class2.major)  
    class2.members = 36  
    fmt.Println(class2.members)  
}

输出结果为:

go 复制代码
{35 Feixin 软件工程 大二 1}
软件工程
0
大数据
36

引用类型

指针

go语言中是有c语言中的指针的机制的,但是他为了安全,有种种限制,go语言中的一般指针是不能进行数学运算的。详情看深度解密Go语言之unsafe - 知乎 (zhihu.com)

  1. *类型:普通指针类型,用于传递对象地址,不能进行指针运算。
  2. unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。
  3. uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。

unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。

这里只介绍普通类型指针,与c指针类似,一样的使用&取地址,使用*取内容,但是不可以进行运算操作,换言之,其实就是在java函数传参基本类型不方便的地方改成了c,但是又不想像c一样那么复杂又不安全就变成了这样。

使用起来与c语言相差不大,例如:

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    a := 3  
    b := &a  
    *b = 2  
    fmt.Println(a)  
}

输出结果为

go 复制代码
2

与c不同的是,数组名不代表数组第一个元素的地址

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    array := [5]int{1, 2, 3, 4, 5}  
    b := &array  
    fmt.Println(*b)  
    c := &array[0]  
    fmt.Println(*c)  

}

输出结果为:

go 复制代码
[1 2 3 4 5]
1

并且不能进行运算


Slice(切片)

Go 语言切片是对数组的抽象。Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

感觉是不是有点像c++中的vector?😶,slice 有长度和容量的概念,也类似于c++中的size 和 capacity

切片的定义

var sliceName []sliceType其实就是[]中没有数量的数组,或者使用make函数

例如:

go 复制代码
var slice []int  
// 使用make函数  
var slice3 = make([]int, 5, 10)  
slice4 := make([]int, 5, 10)  
// 第三个参数可省去
slice5 := make([]int, 10)

使用make函数时,参数分别为slice的数据类型、初始长度、容量,长度内的初始值默认为0,长度外容量内的值虽然有内存空间但不会被初始化,也不能使用。因为是动态数组,容量满了也可以继续增加元素,但是如果容量满了增加元素就会创建一个新的动态数组并把之前的元素全部copy其中,会耗费时间。

切片的初始化
go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    // 类似数组的方法:  
    var slice1 []int = []int{1, 2, 3}  
    slice2 := []int{1, 2, 3}  
    fmt.Println(slice1, slice2)  
    // 利用数组, 区间左闭右开
    array := [5]int{1, 2, 3, 4, 5}  
    slice3 := array[1:]  // 指定开头
    slice4 := array[:3]  // 指定结尾
    slice5 := array[1:3] // 指定区间 
    slice6 := array[:]   // 全部
    fmt.Println(slice3, slice4, slice5, slice6)  
    // 利用切片  
    slice7 := slice6[1:3]  // 上述利用数组的方法都可以,略过不表
    fmt.Println(slice7)  
}

结果为:

go 复制代码
[1 2 3] [1 2 3]
[2 3 4 5] [1 2 3] [2 3] [1 2 3 4 5]
[2 3]
获取切片的长度和容量

可以使用len()函数和cap()函数获取切片的长度和容量

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    slice := []int{1, 2, 3, 4, 5}  
    fmt.Printf("len = %d, cap = %d", len(slice), cap(slice))  
}

结果为:

go 复制代码
len = 5, cap = 5
添加元素(append)

既然是动态数组那么必然少不了我们的添加元素了!还是我们熟悉的append。

append内置函数将元素附加到切片的末尾。如果它有足够的容量,则重新划分目标以容纳新元素。如果没有,将分配一个新的底层数组。Append返回更新后的切片。

语法为append(slice, elements)

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    slice := []int{1, 2, 3, 4, 5}  
    fmt.Println(slice)  
    slice = append(slice, 2)  
    //也可以多个元素append
    //比如append(slice, 2, 3, 4)
    fmt.Println(slice)  
}

结果为:

go 复制代码
[1 2 3 4 5]
[1 2 3 4 5 2]
切片的访问

与数组一样,可以通过下标访问

go 复制代码
func main() {  
    slice := []int{1, 2, 3}  
    fmt.Println(slice[2])  
    // 输出3
}
切片的遍历

可以使用下标遍历,也可以使用for-each遍历

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
  
    strs := []string{"qwq", "qaq", "=.="}  
    for i := 0; i < len(strs); i++ {  
        fmt.Println(strs[i])  
    }  
    for _, i := range strs {  
        fmt.Println(i)  
    }  
}

map

与c++的map类似, 但略有不同

Map 是一种无序的键值对的集合。

Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。

在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""

Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

map的定义
  1. 使用make函数
    make(map[keyType]valueType)
go 复制代码
map1 := make(map[int]int)  
var map2 = make(map[int]int)
  1. 使用map关键字
    map[keyType]valueType{key1:value1, key2:value2, ...}, 这里注意行末一定要打逗号
go 复制代码
map1 := map[string]int{  
    "qwq": 1,  
    "=.=": 2,  // 注意要打逗号
}  
var map2 = map[string]int{  
    "qaq": 1,  
    "0.0": 2,  
}
map的访问

与c++一样的是,我们可以通过map[key]获取到value值, 也可以通过key来修改value的值

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    map1 := map[string]int{  
    "qwq": 1,  
    "qaq": 2,  
    }  
    fmt.Println(map1["qwq"])  
    map1["qwq"] = 3
    fmt.Println(map1["qwq"])
}

输出结果为:

go 复制代码
1
3

不同的是我们可以这样操作

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
    map1 := map[string]int{  
    "qwq": 1,  
    "qaq": 2,  
    }  
    a, ok := map1["qwq"]  
    b, flag := map1["=.="]  
    fmt.Println(a, ok)  
    fmt.Println(b, flag)  
}

输出结果为:

go 复制代码
1 true
0 false

这里第一个变量是返回的值,如果map中不存在该变量,返回的就为value类型的空值,比如这里int就返回0,第二个变量是返回map中是否存在该变量的bool值,如果存在则返回true,如果不存在则返回false

map的遍历

可以使用for-each range来遍历map

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
  
    map1 := map[int]int{  
        0: 48,  
        1: 49,  
        2: 50,  
    }  

    for key, value := range map1 {  
        fmt.Print(key, value)  
        fmt.Print(" ")  
    }  
}

结果为:

go 复制代码
0 48 1 49 2 50
map键值对的删除

我们可以使用delete()函数来删除map中的键值对。

go 复制代码
package main  
  
import "fmt"  
  
func main() {  
  
    map1 := map[string]int{  
        "qwq": 1,  
        "qaq": 2,  
        "=.=": 3,  
    }  
    delete(map1, "qaq")  
    for key, value := range map1 {  
        fmt.Println(key, value)  
    }  
}

结果为:

go 复制代码
qwq 1
=.= 3

函数与结构体方法

golang中的函数允许返回多个值,并且可以为结构体定义方法,类似于java中的类的成员函数

返回单个值函数定义为

func name(参数1, 参数2...) 返回值type {

}

func name(参数1, 参数2...) (返回值1, 返回值2...) {

}

例如:

go 复制代码
package main

import "fmt"

type class struct {
	major       string
	numbers     int
	teacherName string
}

func getTeacherName(cls class) string {
	return cls.teacherName
}

func (c class) getTeacherName2() string {
	return c.teacherName
}

func main() {
	c1 := class{"bigdata", 40, "Feixin"}
	fmt.Println(c1.getTeacherName2())
	fmt.Println(getTeacherName(c1))
}

结果为:

go 复制代码
Feixin
Feixin

空接口 interface

1. 空接口的应用

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数参数。

go 复制代码
// 空接口作为函数参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

空接口作为map的值

使用空接口实现可以保存任意值的字典。

go 复制代码
// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "李白"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)

2. 类型断言

空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

接口值

一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。

这两部分分别称为接口的动态类型动态值

想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:

go 复制代码
x.(T)

其中:

  1. x:表示类型为interface{}的变量
  2. T:表示断言x可能是的类型。

该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

go 复制代码
func main() {
    var x interface{}
    x = "码神之路"
    v, ok := x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型断言失败")
    }
}

上面的示例中如果要断言多次就需要写多个if判断,这个时候我们可以使用switch语句来实现:

go 复制代码
func justifyType(x interface{}) {
    switch v := x.(type) {
    case string:
        fmt.Printf("x is a string,value is %v\n", v)
    case int:
        fmt.Printf("x is a int is %v\n", v)
    case bool:
        fmt.Printf("x is a bool is %v\n", v)
    default:
        fmt.Println("unsupport type!")
    }
}

因为空接口可以存储任意类型值的特点,所以空接口在Go语言中的使用十分广泛。

关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。

相关推荐
源码哥_博纳软云4 分钟前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
学会沉淀。12 分钟前
Docker学习
java·开发语言·学习
西猫雷婶42 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila43 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
初晴~43 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱581361 小时前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城2 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc