99 lines
4.1 KiB
Markdown
99 lines
4.1 KiB
Markdown
## 1.27版本变化
|
||
|
||
### 支持泛型方法
|
||
|
||
Go 1.27 虽然开放了泛型方法,但有明确的限制:
|
||
|
||
1.接口定义中不允许包含泛型方法。
|
||
|
||
2.拥有泛型方法的方法,不能用于实现接口。
|
||
|
||
```go
|
||
// 普通结构体,本身没有任何泛型参数
|
||
type Serializer struct{}
|
||
|
||
// T 属于方法 Serialize,每个调用可以独立拥有不同的 T
|
||
func (s *Serializer) Serialize[T any](data T) string {
|
||
return fmt.Sprintf("%v", data)
|
||
}
|
||
|
||
// 使用:
|
||
sz := Serializer{}
|
||
|
||
// 同一个 sz 实例,每次调用都可以处理完全不同的类型!
|
||
str1 := sz.Serialize("hello") // 自动推导 T = string
|
||
str2 := sz.Serialize(123) // 自动推导 T = int
|
||
str3 := sz.Serialize(true) // 自动推导 T = bool
|
||
```
|
||
|
||
### 结构体字面量:字段选择器当 key
|
||
|
||
嵌套或匿名结构体中的字段,现在可以直接初始化,不必再一层层嵌套字面量
|
||
|
||
```go
|
||
type Person struct {
|
||
Sex string
|
||
Name string
|
||
}
|
||
type Student struct {
|
||
StudentID int
|
||
Person
|
||
}
|
||
|
||
// Go 1.27 之前:必须显式嵌套 Person
|
||
s1 := Student{
|
||
StudentID: 1,
|
||
Person: Person{
|
||
Sex: "male",
|
||
Name: "张三",
|
||
},
|
||
}
|
||
// Go 1.27:嵌套结构体Person的字段可以直接当 key 用
|
||
s1 := Student{
|
||
StudentID: 1,
|
||
Sex: "male",
|
||
Name: "张三",
|
||
}
|
||
```
|
||
|
||
### 函数类型推断:从“手动挡”到“自动挡”
|
||
|
||
```go
|
||
// Go 1.27 之前:必须手动写出类型实参 [int],否则编译报错
|
||
fn := IntToString(FormatToString[int])
|
||
formatter:= []IntToString{FormatToString[int]}
|
||
|
||
// Go 1.27 后:自动推导,不用手写
|
||
fn := IntToString(FormatToString)
|
||
formatter := []IntToString{FormatToString}
|
||
```
|
||
|
||
### 标准库更新:`encoding/json/v2`
|
||
|
||
> Go 1.27 标准库中的 v1 `encoding/json` 内部已经被改用 v2 引擎驱动,保留了 v1 的原始 API 签名和行为
|
||
|
||
新旧版本主要行为对比
|
||
|
||
| **场景** | **encoding/json (v1)** | **encoding/json/v2** |
|
||
| ------------------------------- | -------------------------- | -------------------------------------------- |
|
||
| **`nil` 切片/Map 序列化** | 转为 `null` | 转为 `[]` 和 `{}` *(符合语义,防前端空指针)* |
|
||
| **重复键处理 (Duplicate Keys)** | 允许,后面的覆盖前面的 | **直接报错** *(增强安全性,防 JSON 注入)* |
|
||
| **非法 UTF-8 字符** | 格式化替换,静默忽略 | **直接报错** *(要求严格符合规范)* |
|
||
| **字段名匹配 (大小写敏感)** | 默认**忽略**大小写匹配 | 默认**严格匹配**大小写 *(提升解析性能)* |
|
||
| **固定长度数组解包** | 长度不一致时自动截断或补零 | 必须与 JSON 数组长度严格相等,否则报错 |
|
||
|
||
支持可选项
|
||
|
||
| Options | 作用 |
|
||
| -------------------------------------- | ----------------------- |
|
||
| `json.FormatNilSliceAsNull(true)` | 允许使用 v1 的 nil 行为 |
|
||
| `json.MatchCaseInsensitiveNames(true)` | 允许忽略大小写 |
|
||
|
||
新增标签`omitzero`
|
||
|
||
| **维度** | **omitempty** | **omitzero** |
|
||
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||
| **判断标准** | **是否为“空 (Empty)”** (如:空字符串 `""`、长度为 0 的切片/Map、`false`、`0`、指针 `nil`) | **是否为“ Go 类型的零值 (Zero Value)”** (满足 `reflect.Value.IsZero()` 或实现 `IsZero() bool` 接口) |
|
||
| **结构体/时间类型处理** | **有重大缺陷**,对非指针的结构体(如 `time.Time`)无效!依然会被序列化。 | **完美支持** 未初始化的 `time.Time{}` 或任何嵌套结构体零值均会被正确跳过。 |
|
||
| **自定义类型零值** | 不支持自定义零值判断逻辑。 | 支持只要实现 `IsZero() bool` 方法即可自定义是否跳过。 |
|