为什么Go语言方法不支持泛型?(新手必看)
如果单从全局函数支持泛型的类型参数开始推导,Go 语言的方法自然也应该支持泛型,而且这个特性在编译时可以和谐存在。但是 Go 1 最开始是围绕接口设计的运行时泛型,而接口通过在运行时判断规则与静态方法集形成完全正交的设计。
在引入泛型后,Go 语言选择了通过扩展接口的语法来实现编译时的类型参数约束。例如:
针对不同类型特化的 IPinter 接口(如 IPinter[int] 和 IPinter[float32])会产生两个不同签名的 Print() 方法,它们的返回值参数分别是 int 和 float32,从运行时接口看,这两个是不同的方法。
根据目前 Go 语言接口的运行机制,同一个方法名只能有一个确定的函数签名。这导致了接口在编译时和运行时语义的矛盾,这也是泛型引入带来的核心设计哲学的分裂。
为了避免泛型接口的方法在编译时和运行时分裂(接口的方法因为 T 类型参数导致了签名的差异),Go 语言在方法的定义一侧禁止了泛型的类型参数特性,与此同时也象征性地禁止了接口中方法的类型参数。
方法和接口分别代表编译时和运行时两端,Go 语言通过微妙的部分禁止方法泛型和扩展接口的部分泛型特性,来避免在编译时类型约束和运行时方法同时出现泛型参数而引起的语义矛盾。
Go 语言的方法不支持泛型参数是为了适配接口不能在运行时支持静态泛型的妥协。此外,在泛型类型的参数类型约束中,无法通过接口明确泛型方法的类型参数,这将导致泛型函数中无法静态使用泛型的方法。
因此,泛型方法只能在它所依附的类型彻底特化之后被调用,这将是一个比较孤立的特性。可能是两个因素最终影响了设计,方法的泛型也最终被禁止。
泛型可以提高代码的可复用性,但它也引入了一些的新复杂性和潜在的问题,对 Go 语言原本相对和谐、正交的设计产生了根本性的破坏。
在引入泛型后,Go 语言选择了通过扩展接口的语法来实现编译时的类型参数约束。例如:
type IPinter[T any] interface { Print(v T) } func MyPrint[T IPinter[T]](v T) { v.Print(v) }泛型工作在编译时阶段,MyPrint 全局函数的 T 类型通过 IPinter[T] 来约束。接口要求 T 类型必须带有一个 Print() 方法,而该方法的参数 T 也是类型参数。
针对不同类型特化的 IPinter 接口(如 IPinter[int] 和 IPinter[float32])会产生两个不同签名的 Print() 方法,它们的返回值参数分别是 int 和 float32,从运行时接口看,这两个是不同的方法。
根据目前 Go 语言接口的运行机制,同一个方法名只能有一个确定的函数签名。这导致了接口在编译时和运行时语义的矛盾,这也是泛型引入带来的核心设计哲学的分裂。
为了避免泛型接口的方法在编译时和运行时分裂(接口的方法因为 T 类型参数导致了签名的差异),Go 语言在方法的定义一侧禁止了泛型的类型参数特性,与此同时也象征性地禁止了接口中方法的类型参数。
方法和接口分别代表编译时和运行时两端,Go 语言通过微妙的部分禁止方法泛型和扩展接口的部分泛型特性,来避免在编译时类型约束和运行时方法同时出现泛型参数而引起的语义矛盾。
Go 语言的方法不支持泛型参数是为了适配接口不能在运行时支持静态泛型的妥协。此外,在泛型类型的参数类型约束中,无法通过接口明确泛型方法的类型参数,这将导致泛型函数中无法静态使用泛型的方法。
因此,泛型方法只能在它所依附的类型彻底特化之后被调用,这将是一个比较孤立的特性。可能是两个因素最终影响了设计,方法的泛型也最终被禁止。
泛型可以提高代码的可复用性,但它也引入了一些的新复杂性和潜在的问题,对 Go 语言原本相对和谐、正交的设计产生了根本性的破坏。