示例
首先模拟一个业务场景,有订单、产品、自定义订单三个结构体,订单中包含多个产品:
1 | type Order struct { |
初始化模拟数据:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28var orders = []Order{
{
Id: "o1",
Products: []Product{
{
Id: "p1",
Price: 1,
},
{
Id: "p2",
Price: 2,
},
},
},
{
Id: "o2",
Products: []Product{
{
Id: "p3",
Price: 3,
},
{
Id: "p4",
Price: 4,
},
},
},
}
接下来对订单列表做各种操作:
1 | // 过滤Id为o2的订单 |
原理
1 | type List[T any] struct { |
将 go 中的原生切片包装成 List[T]
结构体,特别说明其中的泛型 T
是最终结果的元素类型,并不是原始传入切片的类型。
这样设计是因为 go 只能在构造结构体时指定泛型,因此将 List[T]
的泛型指定为最终结果的元素类型,就可以在操作完成后调用 Collect()
方法,得到最终的 T
类型切片,方便后面的业务逻辑使用。
因为 go 不支持在接受者函数中定义泛型,因此所有操作函数的参数和返回值类型只能定义为any
,然后在函数体内转换为业务结构体使用,例如上面的 i.(Product).Price
。
此后将每一种操作,例如Filter、Map、Flat等,都返回List[T]
结构体,就可以实现链式操作。
实现
1 | type List[T any] struct { |