如何讲我的服务开放给用户 | 青训营

53 阅读7分钟

Go语言允许用户定义类型。当用户声明一个新类型时,这个声明就给编译器提供了一个框架,告知必要的内存大小和表示信息。声明后的类型与内置类型的运作方式类似。Go语言里声 明用户定义的类型有两种方法。最常用的方法是使用关键字struct,它可以让用户创建一个结构类型。结构类型通过组合一系列固定且唯一的字段来声明,如代码清单 5-1所示。结构里每个字段都会用一个已知类型声明。这个已知类型可以是内置类型,也可以是其他用户定义的类型。 代码清单5-1 声明一个结构类型 01 // user在程序里定义一个用户类型02 type user struct { 03 name string 04 email string 05 ext int 06 privileged bool 07 } 在代码清单5-1中,可以看到一个结构类型的声明。这个声明以关键字type开始,之后是新类型的名字,最后是关键字struct。这个结构类型有4个字段,每个字段都基于一个内置类型。读者可以看到这些字段是如何组合成一个数据的结构的。一旦声明了类型(如代码清单5-2所示),就可以使用这个类型创建值。 代码清单5-2 使用结构类型声明变量,并初始化为其零值09//声明user类型的变量10 var bill user 在代码清单 5-2的第 10 行,关键字 var创建了类型为user 且名为bill的变量。当声明变量时,这个变量对应的值总是会被初始化。这个值要么用指定的值初始化,要么用零值(即变量类型的默认值)做初始化。对数值类型来说,零值是0;对字符串来说,零值是空字符串;对布尔类型,零值是false。对这个例子里的结构,结构里每个字段都会用零值初始化。 任何时候,创建一个变量并初始化为其零值,习惯是使用关键字 var。这种用法是为了更明确地表示一个变量被设置为零值。如果变量被初始化为某个非零值,就配合结构字面量和短变量声明操作符来创建变量。 代码清单5-3展示了如何声明一个user类型的变量,并使用某个非零值作为初始值。在第13 行,我们首先给出了一个变量名,之后是短变量声明操作符。这个操作符是冒号加一个等号(:=)。一个短变量声明操作符在一次操作中完成两件事情:声明一个变量,并初始化。短变量声明操作符会使用右侧给出的类型信息作为声明变量的类型。 代码清单5-3 使用结构字面量来声明一个结构类型的变量 12 // 声明user类型的变量,并初始化所有字段13 lisa := user{ 14 15 name: emai1: "Lisa", "lisa0email.com”,16 ext: 123, 17 privileged: true, 18 } 既然要创建并初始化一个结构类型,我们就使用结构字面量来完成这个初始化,如代码清单5-4示。结量使用一大号括住内部字段的初始值。代码清单54 使用结构字面量创建结构类型的值 13 user{ 16 14 15 ext: name: email: 123, "Lisa", "lisa@email.com", 17 privileged: true, 18 } 结构字面量可以对结构类型采用两种形式。代码清单5-4中使用了第一种形式,这种形式在不同行声明每个字段的名字以及对应的值。字段名与值用冒号分隔,每一行以逗号结尾。这种形式对字段的声明顺序没有要求。第二种形式没有字段名,只声明对应的值,如代码清单5-5所示。 代码清单5-5 不使用字段名,创建结构类型的值12// 声明user类型的变量 13 lisa := user{"Lisa","lisa@email.com",123, true} 每个值也可以分别占一行,不过习惯上这种形式会写在一行里,结尾不需要逗号。这种形式下,值的顺序很重要,必须要和结构声明中字段的顺序一致。当声明结构类型时,字段的类型并不限制在内置类型,也可以使用其他用户定义的类型,如代码清单 5-6所示。 代码清单5-6 使用其他结构类型声明字段 20// admin需要一个user类型作为管理者,并附加权限21 type admin struct ( 22 person user 23 level string 24 } 代码清单5-6展示了一个名为admin的新结构类型。这个结构类型有一个名为person的 user类型的字段,还声明了一个名为level的string字段。当创建具有person这种字的结构类型的变量时,初始化用的结构字面量会有一些变化,如代码清单5-7所示。 代码清单5-7 使用结构字面量来创建字段的值26 // 声明admin 类型的变量27 fred := admin{ 2928 person: userl name:"Lisa", 30 email: "lisa@email.com", 31 err: 123, 32 privileged: true,33 }, 34 level: "super", 35}为了初始化person字段,我们需要创建一个user类型的值代码清单5-7的第8是在创建这个值。这行代码使用结构字面量的形式创建了一个user类型的值,并赋给了per字段。另一种声明用户定义的类型的方法是,基一个已有类,将新类型的类型设当需要一个可以用已有类型表示的新类型的时候,这种方法会非常好用,如代码清单 5-8所示标准库使用这种声明类型的方法,从内置类型创建出很多更加明确的类型,并赋予更高级的功 代码清单5-8 基于int64声明一个新类型 type Duration int64 代码清单5-8展示的是标准库的time包里的一个类型的声明。Duration是一种描述时间间隔的类型,单位是纳秒(n)类型使用内置的int64表示。Duration类型的声明中,我们把int64 类型叫作Duration的基础类型。不过,虽然int64是基础类型,Go并不认为Duration和int4同种类。个类型完全不同的有区别的类型。 为了更好地展示这种区别,来看一下代码清单5-9所示的小程序。这个程序本身无法通过编译。 代码清单5-9 给不同类型的变量赋值会产生编译错误01 package main02 03 type Duration int6404 05 func main()( 06 var dur Duration 07 dur-int64(1000) 08 } 代码清单5-9所示的程序在第03行声明了Duration类型。之后在第06行声明了一个类型为Duration的变量dur,并使用零值作为初值。之后,第07行的代码会在编译的时候产生编译错误,如代码清单5-10所示。 代码清单5-10实际产生的编译错误 prog.go:7: cannot use int64(1000) (type int64) as type Duration in assignment 编译器很清楚这个程序的问题:类型int64值能urtion。话说,虽然int4型是基类型,Duration类型依然是一个独立的类型。两种不同类型的值即便互相兼容,也不能互相赋值。编译器不会对不同类型的值做隐式转换。

接口 多态是指代码可以根据类型的具体实现采取不同行为的能力。如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。标准库里有很好的例子,如io包里实现的流式处理接口。io包提供了一组构造得非常好的接口和函数,来让代码轻松支持流式数据处理。只要实现两个接口,就能利用整个io包背后的所有强大能力。 不过,我们的程序在声明和实现接口时会涉及很多细节。即便实现的是已有接口,也需要了解这些接口是如何工作的。