Go 的范型怎么把 Response[指定类型] 转换为 Response[any]

2023-01-28 10:52:36 +08:00
 jorneyr

问题: Go 的范型怎么把 Response[指定类型] 转换为 Response[any].

下面的示例代码在 [3] 部分进行类型转换的时候报错 cannot use rsp2 (variable of type Response[map[string]string]) as Response[any] value in assignment 。

package main

import (
	"encoding/json"
	"fmt"
)

type Response[T any] struct {
	Code    int    `json:"code"`           // 业务逻辑相关的 code ,不是 HTTP Status Code
	Success bool   `json:"success"`        // 业务逻辑处理成功时为 true ,错误时为 false
	Msg     string `json:"msg"`            // 请求的描述
	Data    T      `json:"data,omitempty"` // 请求的 payload
}

func main() {

}

func foo[T any]() Response[any] {
	// [1] 创建范型对象
	rsp1 := Response[map[string]string]{
		Code:    200,
		Success: true,
		Data:    map[string]string{"1": "one", "2": "two"},
	}

	// [2] 范型序列化
	b, _ := json.Marshal(rsp1)
	fmt.Println(string(b))

	// [3] 范型反序列化
	var rsp2 Response[map[string]string]
	json.Unmarshal(b, &rsp2)
	fmt.Printf("%+v\n", rsp2)

	// [4] 范型向上类型转换
	// 相当于 Java 的范型用 ? 来接收任意类型的范型如
	//     List<String> list1 = new LinkedList<>();
	//     List<?> list2 = list1;
	rsp3 := Response[any]{}
	rsp3 = rsp2 // 错误: cannot use rsp2 (variable of type Response[map[string]string]) as Response[any] value in assignment

	return rsp3
}
2658 次点击
所在节点    程序员
29 条回复
nobot
2023-01-28 11:01:06 +08:00
返回值需要明确知道类型,不然编译器,无法特例化对应类型的函数
jorneyr
2023-01-28 11:05:21 +08:00
@nobot 还不到返回值类型的地方,rsp3 = rsp2 赋值的时候就报错了。
nobot
2023-01-28 11:06:23 +08:00
返回 Response[T]
@jorneyr
hahadaxigua834
2023-01-28 11:12:40 +08:00
func (x *Response[T]) ToAny() Response[any] {
return Response[any]{
Code: x.Code,
Success: x.Success,
Msg: x.Msg,
Data: x.Data,
}
}
jorneyr
2023-01-28 11:12:58 +08:00
@nobot 我想定义一个函数,接收任意类型的 Response[any],然后处理后返回给客户端,所以这个函数的目录就是接收任意类型的 Response ,也就是上面用 rsp3 = rsp2 进行演示。

返回 Response[T] 满足不了需求。
rrfeng
2023-01-28 11:17:11 +08:00
你都上泛型了为啥要 any……是不是跑偏了
kaf
2023-01-28 11:17:14 +08:00
编译错误说明了 Response[map[string]string]和 Response[any]是两个不同类型的参数
jorneyr
2023-01-28 11:17:29 +08:00
@hahadaxigua834
谢谢,返回前调用这个方法转换一下能满足要求。
hahadaxigua834
2023-01-28 11:18:07 +08:00
@hahadaxigua834 最快的方法就是做个 copy
jorneyr
2023-01-28 11:18:29 +08:00
@kaf Java 的范型 ? 可以接收任意类型的,就想看看 Go 里能不能也用相似的办法实现。
kaf
2023-01-28 11:21:57 +08:00
@jorneyr
func foo[T any](in T) Response[T] {
// [1] 创建范型对象
rsp1 := Response[T]{
Code: 200,
Success: true,
Data: in,
}
return rsp1
}
你是说这样的功能吗,go 的泛型使用就是你确定入参和返回类型的情况下
jorneyr
2023-01-28 11:28:46 +08:00
@kaf
// 像下面这样,接收 Response[string], Response[int] 等任意类型的范型参数进行统一处理。
// 业务代码里可能生成 Response[string], Response[AgentStats] 等不同类型的响应对象,这些对象都会在下面的 responseCommonHandle 函数中统一处理例如某些情况下打印日志。
func responseCommonHandle(rsp Rsponse[any]) {

}
OuJin
2023-01-28 11:28:48 +08:00
@jorneyr

func main() {

// 返回数据
var (
response1 = Response[map[string]string]{} // 返回 1, 数据类型 map[string]string
response2 = Response[map[int]int]{} // 返回 2, 数据类型 map[int]int
// ...
)

// 模拟接收到的数据
var (
body1, _ = json.Marshal(response1)
body2, _ = json.Marshal(response2)
)

// 解析
var (
result1, err1 = foo[map[string]string](body1) // 指定 T 类型为 map[string]string
result2, err2 = foo[map[int]int](body2) // 指定 T 类型为 map[int]int
)

fmt.Println(result1, err1)
fmt.Println(result2, err2)
}

func foo[T any](body []byte) (response Response[T], err error) {
err = json.Unmarshal(body, &response)
return
}

在调用 foo 时指定类型,看看这样满不满足要求
jorneyr
2023-01-28 11:36:53 +08:00
@OuJin 谢谢,我的问题重点不是在序列化和反序列化方面 (提问的时候应该去掉,加上只是为了验证序列化功能在范型的时候可以正常使用)。

我的问题主要是在不知道 Go 里有没有一个像 Java 范型那样: 定一个范型类型,可以接收任意类型的范型对象,也就是下面这个例子:
List<String> list1 = new LinkedList<>();
List<?> list2 = list1;
bigboNed3
2023-01-28 11:40:21 +08:00
@jorneyr
```
// [4] 范型向上类型转换
// 相当于 Java 的范型用 ? 来接收任意类型的范型如
// List<String> list1 = new LinkedList<>();
// List<?> list2 = list1;
rsp3 := Response[any]{Code: rsp2.Code, Success: rsp2.Success, Data: rsp2.Data}

```
kaf
2023-01-28 11:40:22 +08:00
@jorneyr 下面这段代码是否是你想要的功能
```
package main

// 定义一个结构体,Data 是一个泛型接口
type Response[T ResponseHandle] struct {
Code int `json:"code"` // 业务逻辑相关的 code ,不是 HTTP Status Code
Success bool `json:"success"` // 业务逻辑处理成功时为 true ,错误时为 false
Msg string `json:"msg"` // 请求的描述
Data T `json:"data,omitempty"` // 请求的 payload
}

type ResponseHandle interface {
log()
}

type AgentStats struct {
Status int
}

func (r *AgentStats) log() {
// do something
}

func main() {

}

// 输入泛型的 resp,在函数中执行相关的方法
func responseCommonHandle[T ResponseHandle](rsp Response[T]) {
rsp.Data.log()
}
```
jorneyr
2023-01-28 11:44:35 +08:00
@kaf 不是的,如果用接口的方式,每个类型都要实现接口,反而工作量更大了,只是想要一个纯粹的存放数据的结构体。
lysS
2023-01-28 11:47:47 +08:00
T[any] 这种泛型没啥意义,还有进一步的性能损失,不如直接 any 断言
kaf
2023-01-28 11:51:38 +08:00
@jorneyr 那其实输出类型 T ,返回类型 T 即可,你应该使用 T 类型而不是使用 any ,any 只是封装的 interface 类型,go 的泛型并不是 Java 的泛型,Java 的所有对象继承于 Object ,在 go 中每个结构体都是单独的类型,并不能强转,而且你需要在函数定义是知道输入什么类型,类似于 interface 可以接受任意类型参数,而定义泛型之后,编译器知道了 interface 是你定义的泛型结构体中的一个
kaf
2023-01-28 11:54:32 +08:00
@jorneyr 可以看 go 实现 Java 的 stream 的 map 方法
// Map manipulates a slice and transforms it to a slice of another type.
// Play: https://go.dev/play/p/OkPcYAhBo0D
func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
result := make([]R, len(collection))

for i, item := range collection {
result[i] = iteratee(item, i)
}

return result
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/910999

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX