练习
学完本章你应该掌握
- 能区分自定义类型和类型别名,理解“是否产生新类型”会影响赋值、比较、传参和方法绑定。
- 能使用
type 新类型 底层类型为普通值补充业务语义,并通过常量集中表达状态、等级等枚举值。 - 能在需要时完成自定义类型与底层类型之间的显式转换,避免直接混用导致的编译错误。
- 能判断什么时候更适合使用类型别名,什么时候更适合使用自定义类型,而不是为了“换个名字”随意定义。
- 能把本章知识自然放进函数、结构体、切片、map 和控制流程里,写出语义更清晰的基础程序。
- 能识别并修正常见错误,例如漏写
=、把自定义类型直接和底层类型比较、把别名误当成新类型使用。
简单
第 1 题:定义一个订单状态自定义类型
编写一个 Go 程序,完成下面要求:
- 定义自定义类型
OrderStatus,底层类型是int - 定义两个常量:
StatusPending = 1StatusPaid = 2 - 在
main中定义status := StatusPaid - 先输出它对应的整数值
- 再定义
raw := 2 - 判断
status和raw是否表示同一个值,并输出结果
要求:比较时请使用显式类型转换。
查看参考答案
| |
输出结果:
| |
说明:OrderStatus 是一个新类型,虽然底层是 int,但和 int 变量比较时仍应先显式转换。
第 2 题:判断哪些写法可以通过编译
下面这些写法里,哪些可以通过编译,哪些不能?请分别写出结果,并简要说明原因。
| |
查看参考答案
可以通过编译的是:
| |
不能通过编译的是:
| |
原因说明:
A合法,200可以赋值给StatusCode。B不合法,StatusCode是新类型,不能直接赋值给int。C合法,先转成int(a)就可以。D合法,AliasCode是int的别名,本质上还是int。E合法,别名和原类型可以直接赋值。F不合法,a是StatusCode,raw是int变量,二者不能直接比较。
第 3 题:修正自定义类型和整数混用错误
下面程序想输出:
| |
但它现在不能通过编译。请改正后让它正常运行。
| |
查看参考答案
修正后的代码如下:
| |
关键点:
Price是自定义类型,不能直接和int做加法。- 比较时也要保证两边类型一致,所以这里同样要先转换。
第 4 题:使用类型别名判断编辑权限
编写一个 Go 程序,完成下面要求:
- 定义类型别名
UserID = int - 定义
ownerID,值为1001 - 定义
currentID,值为1001 - 如果两者相等,输出
当前用户可以编辑 - 否则输出
无权限
要求:直接比较,不要额外做类型转换。
查看参考答案
| |
说明:UserID 是 int 的别名,所以这里可以直接和 int 比较。
第 5 题:给会员等级自定义类型绑定方法
编写一个 Go 程序,完成下面要求:
- 定义自定义类型
MemberLevel,底层类型是int - 定义三个常量:
LevelNormal = 1LevelVIP = 2LevelSVIP = 3 - 给
MemberLevel绑定方法Name() string - 返回规则如下:
LevelNormal返回普通会员LevelVIP返回VIP会员LevelSVIP返回SVIP会员其他值返回未知等级 - 在
main中输出LevelVIP.Name()
查看参考答案
| |
输出结果:
| |
说明:自定义类型可以绑定方法,这也是它和“只是换个名字”的类型别名之间很重要的区别。
一般
第 6 题:用自定义分数类型统计总分和平均分
编写一个程序,完成下面要求:
- 定义自定义类型
Score,底层类型是int - 编写函数
calcStats(scores []Score) (total int, avg float64) - 在函数中使用
for range统计总分和平均分 - 准备下面这个切片:
| |
- 在
main中调用这个函数 - 输出:
| |
查看参考答案
| |
说明:Score 虽然底层是 int,但在累加到 int total 时仍要显式转换,这正是自定义类型“语义更清晰,但不会自动混用”的体现。
第 7 题:用别名和自定义类型组织订单查询
编写一个程序,完成下面要求:
- 定义类型别名
OrderID = int - 定义自定义类型
OrderStatus,底层类型是int - 定义三个状态常量:
StatusPending = 1StatusPaid = 2StatusShipped = 3 - 给
OrderStatus绑定方法Text() string - 定义结构体
Order,字段包括:ID OrderIDBuyer stringStatus OrderStatus - 准备一张订单表:
| |
- 读取一个订单编号
- 如果存在,输出:
| |
- 如果不存在,输出:
| |
查看参考答案
| |
说明:这道题里 OrderID 用别名是为了保留和 int 的直接兼容;OrderStatus 用自定义类型是为了让状态值拥有自己的语义和方法。
第 8 题:修正把别名写成自定义类型的代码
下面程序的设计目标是:
- 用户输入的是普通
int - 这个
int可以直接拿来查询users这张表 - 不希望每次查表时都手动做类型转换
但它现在不能通过编译。请改正代码,并保持上面的设计目标不变。
| |
查看参考答案
修正后的代码如下:
| |
原因说明:
- 原来的
type UserID int定义的是自定义类型,不是别名。 - 如果目标是“让
int可以直接使用”,就应该写成type UserID = int。 - 这道题的重点不是语法细节本身,而是先想清楚这个场景到底要“新类型”还是要“兼容旧类型”。
进阶
下面两题会把前面学过的函数、结构体、切片、map、判断和循环一起用起来,重点考察你是否真的能把“自定义类型”和“类型别名”放进更完整的数据建模场景里。
第 9 题:统计任务状态数量
请编写一个程序,完成下面要求:
- 定义自定义类型
TaskStatus,底层类型是int - 定义三个状态常量:
StatusTodo = 1StatusDoing = 2StatusDone = 3 - 给
TaskStatus绑定方法Text() string - 定义结构体
Task,字段包括:Name stringStatus TaskStatus - 准备下面这组任务数据:
| |
- 遍历任务切片,用
map[TaskStatus]int统计每种状态的数量 - 最后按下面顺序输出:
| |
查看参考答案
| |
说明:这道题里,自定义类型不只是“看起来更清楚”,它还直接参与了方法调用、切片字段建模和 map 统计键的设计。
第 10 题:实现一个学员等级报告
请完成一个稍微完整一点的小场景:
- 定义类型别名
StudentID = int - 定义自定义类型
ResultLevel,底层类型是int - 定义四个等级常量:
LevelALevelBLevelCLevelD - 给
ResultLevel绑定方法Text() string - 编写函数
judge(score int) ResultLevelscore >= 90返回LevelAscore >= 80返回LevelBscore >= 60返回LevelC其他返回LevelD - 定义结构体
Student,字段包括:ID StudentIDName stringScore int - 准备下面这组学生数据:
| |
- 遍历学生切片,逐行输出每个学生的编号、姓名和等级
- 再使用
map[ResultLevel]int统计各等级人数 - 最后按
A / B / C / D的顺序输出统计结果
示例输出可以类似这样:
| |
查看参考答案
| |
说明:这道题把“别名适合兼容旧类型”“自定义类型适合承载业务语义”这两个选择放进同一个小项目里了。StudentID 保持了和 int 的直接兼容,ResultLevel 则承担了等级表达、方法调用和统计分组这些更明确的业务职责。