Go语言中的泛型

Go语言中的泛型

本文介绍了Go中泛型的基础知识。使用泛型,你可以声明和使用具有为调用代码提供的一组类型的函数或类型。在下面,我们将声明两个简单的非泛型函数,然后我们用一个泛型函数来实现相同的功能。

我们将完成以下几个部分:

  1. 为我们的代码先创建一个文件夹
  2. 添加非泛型函数方法
  3. 添加泛型函数方法来处理多类型
  4. 当调用泛型函数方法时移除类型参数
  5. 声明一个类型约束

创建代码文件夹

这里我就将文件夹命名为 generics ,然后我们进入到该文件夹并初始化模块

cd generics
go mod init example/generics

添加非泛型方法

在这一步中,我们将添加两个函数,每一个函数将map中的数相加并返回。

编写代码

main.go

package main

func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

上面的代码中的两个方法分别是对都以字符串为key,分别以int64和float64为value的数进行求和并返回对应的数据。下面我们编写主方法并在里面初始化两个map然后调用上面的方法

func main() {
    ints := map[string]int64{
        "first": 34,
        "second": 12,
    }

    floats := map[string]float64{
        "first": 35.98,
        "second": 26.99
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n", SumInts(ints), SumFloats(floats))
}

由于上面我们使用了 fmt 进行打印,因此需要引入 fmt 标准库

package main

import "fmt"

运行代码

使用 go run .命令运行代码

go run .

运行结果

Non-Generic Sums: 46 and 62.97

添加泛型方法处理多类型

上面使用了两个方法实现类类似的功能,只不过它们的参数类型不同,如果一两个这样的操作你觉得无所谓,但是当数据类型很多时如果实现的功能相同那上面的做法很不优雅,会产生大量的冗余代码,下面我们将使用一个方法来实现上面的功能,该方法允许int64或float64作为参数类型

func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for -, v := range m {
     s += v
    }
    return s
}

上面的代码中声明了一个SumIntsOrFloats函数,该函数具有两个类型参数(方括号里面的)K和V,以及一个使用类型参数的形参集map[K]V的m,该函数返回V类型的值。

给K指定了 comparable 类型的参数,Go要求map的Key值具有可比性,因此将它设置为可比较的类型是必要的,这还确保了调用者对map简直的类型使用。

给V指定了两种类型的并集,即int64和float64,只要符合其中的一种类型即可,形参m指定为了map[K]V,这里我们确定map是一个有效的map,因为在前面已经对K做了可比较类型的约束。下面我们修改主方法:

fmt.Printf("Generic Sums: %v and %v\n",
    SumIntsOrFloats[string, int64](ints),
    SumIntsOrFloats[string, float64](floats))

运行代码

go run .

运行结果

Generic Sums: 46 and 62.97

可以看到,是同样的结果,但是我们使用了泛型函数只需要一个函数就可以。

调用时移除类型参数

在上面的main方法中我们在调用泛型方法时加了参数类型,也就是中括号里面的内容[string, int64]来告诉泛型函数我们调用时传递的参数类型,但其实Go编译器在编译的时候是可以根据方法参数推断出参数的类型,因此我们可以省略不写。但是注意:这并不总是可能的,如果被调用的泛型函数没有参数,此时我们在调用方法的时候应该包含参数类型。
下面我们修改一下main.go的代码:

fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
	SumIntsOrFloats(ints),
	SumIntsOrFloats(floats))

下面我们运行代码,这次我把上面的两个打印都放开,对比一下,结果是:

Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97

可以看到,当我们省略了参数类型的时候依然正确的运行了程序,说明Go编译的时候自动推断出了参数的类型。

声明类型约束接口

下面,我们将把前面定义的约束移动到我们自己的接口中,这样我们就可以在多个地方重用它,这种声明方式有助于简化代码,例如当约束更复杂时。
当我们将类型约束声明为接口,约束允许实现接口的任何类型。例如,如果用三个方法声明类型约束接口,然后将其与泛型函数中的类型参数一起使用,则用于调用该函数的类型参数被许具有所有的这些方法。
约束接口也可以引用特定的类型,例如我们下面这样使用

type Number interface {
    int64 | float64

}
在上面的代码中,我们声明了一个名为Number的约束接口,在接口内声明了int64float64的并集,下面当我们需要使用`int64 | float64`这样的约束类型时就可以使用Number这个类型,而不需要写`int64 | float64`代码如下
~~~go
func SumNumbers[K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

然后我们将main.go中添加新方法的打印

fmt.printf("Generic Sums with Constraint: %v and %v\n",
	SumNumbers(ints),
	SumNumbers(floats))

然后我们运行代码,结果如下:

Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97
Generic Sums with Constraint: 46 and 62.97

本文最终完整代码

package main

import "fmt"

type Number interface {
	int64 | float64
}

//Non-Generic Function

func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}
	return s
}

func SumFloat(m map[string]float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

// Generic Function

func SumIntsOrFloats [K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func SumNumbers [K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

func main() {
	ints := map[string]int64 {
		"first": 34,
		"second": 12,
	}
	floats := map[string]float64 {
		"first": 35.98,
		"second": 26.99,
	}

	fmt.Printf("Non-Generic Sums: %v and %v\n", SumInts(ints), SumFloat(floats))
	fmt.Printf("Generic Sums: %v and %v\n", SumIntsOrFloats[string, int64](ints), SumIntsOrFloats[string, float64](floats))
	fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n", SumIntsOrFloats(ints), SumIntsOrFloats(floats))
	fmt.Printf("Generic Sums with Constraint: %v and %v\n", SumNumbers(ints), SumNumbers(floats))

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Golang 是不支持泛型的,所以你不能使用泛型。但是你可以使用类似于泛型的方法来实现类似的效果。 你可以使用 interface{} 类型来表示任何类型。例如: ``` func Print(a interface{}) { fmt.Println(a) } ``` 这样就可以使用 Print 函数输出任何类型的值了。 你也可以使用反射来实现类似的效果。例如: ``` func Print(a interface{}) { v := reflect.ValueOf(a) fmt.Println(v) } ``` 这样就可以使用 Print 函数输出任何类型的值了。 你也可以使用模板函数来实现类似的效果。例如: ``` func Print(a interface{}) { t := template.Must(template.New("").Parse("{{.}}")) t.Execute(os.Stdout, a) } ``` 这样就可以使用 Print 函数输出任何类型的值了。 希望这对你有帮助。 ### 回答2: 截至2021年1月,Go语言(Golang)尚不具备内建的泛型机制,即没有直接的方法来创建通用的数据结构和算法。然而,Go社区一直在探索如何在缺少泛型的情况下实现通用性。 在没有泛型的情况下,开发者通常采用特定类型的接口(interface{})或类型断言(type assertion)来实现类似泛型的效果。使用接口可以定义通用的函数签名,使函数能够接受多种类型参数。同时,使用类型断言可以在运行时检查参数的实际类型,并进行相应的处理。 此外,Go语言还提供了一些通用的数据结构和算法库,如container包的List、HashMap等。通过在这些数据结构和算法使用interface{}类型,并利用类型断言来确保类型的正确性,可以实现相对通用的功能。 Go语言开发团队也一直在推动泛型的研究和开发。在2022年将发布的Go 1.18版本,预计将加入泛型的支持。该特性将为开发者提供使用更为简洁和安全的方式来创建通用的数据结构和算法。 总之,目前在Go语言还没有内建的泛型机制,但可以通过接口和类型断言来实现类似的功能。此外,Go语言开发者正在努力研发并计划在未来的版本添加泛型支持。 ### 回答3: 目前(截至2021年),Go语言Golang)尚不支持原生泛型,这意味着在编写Go代码时,无法直接声明泛型类型或方法。然而,Go社区一直在积极探索和讨论如何实现泛型,有一些可选方案可以用来模拟泛型的使用。 其,最常见的一种方法是使用接口类型实现泛型。通过创建一个接口类型并在函数签名或结构体使用该接口类型作为参数或字段,可以实现对各种类型的参数和字段的通用操作。这种方法虽然可以达到类似泛型的效果,但在类型实参和类型断言方面存在一些限制和复杂性。 另一种方法是使用代码生成工具模拟泛型。通过在编译时使用代码生成工具,可以根据不同的类型参数生成特定的代码,并将其插入到源代码。这种方式可以实现在编译时生成针对不同类型的特定代码,从而实现类似泛型的效果。但是,这种方法需要使用额外的工具和开发流程,并且会增加代码的复杂性。 除了这些方法外,Go社区还在不断探索其他更为原生的泛型实现方式,例如Go2泛型的设计,该设计目前正在积极开发。Go2泛型通过添加泛型类型和泛型函数的直接支持,将大大简化使用泛型的过程,并提供更好的类型安全和可读性。尽管Go2泛型还未正式发布,但对于在项目使用泛型的需求,可以关注最新的Go官方进展和社区讨论。 总的来说,目前在Go语言,尚不支持原生的泛型,但可以使用接口类型或代码生成工具等方式来模拟泛型的使用。未来,随着Go2泛型的正式发布,将会提供更为便捷和直接的方式来使用泛型。环境和项目需求决定了选择合适的方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@胡海龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值