golang泛型:generics

使用非泛型函数

golang在没有泛型的时候,如果我们要计算一个map的所有value之和,比如该map的key为string类型,value是int64类型,我们需要定义以下函数:

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

同时我们又有另外一个map也需要计算所有value之和,该map的key也为string类型,但是value是float64类型,所以我们还需要定义另外一个函数:

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

然后在分别调用两个函数执行计算操作

1. 先定义两个map

// Initialize a map for the integer values
ints := map[string]int64{
    "first": 34,
    "second": 12,
}

// Initialize a map for the float values
floats := map[string]float64{
	"first": 35.98,
	"second": 26.99,
}

2. 调用函数

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

可以发现SumInts和SumFloats除了接收参数不同之外内部逻辑完全相同,如果有更多参数类型不不同但功能逻辑完全的功能,会发现到处都是重复的代码,所以就有了泛型来解决这个问题

使用泛型函数

定义泛型函数

实现同样的计算一个map的value之和,可以定义如下函数:

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}
  • 该函数声明了两个类型参数,即[]中的K和V,和一个要使用这两个类型参数的map类型的参数m,返回一个V类型的值
  • 其中comparable是一个在go里面预定义的类型,表示K这个参数需要支持== 和 != 操作
  • 所以可理解为该函数接收一个map参数,该map的key是一个comparable类型的值,value是int64或者float类型的值,所以定义一个map[int]int64也是可以用这个函数计算的,因为int类型也是一个comparable类型的参数,可以做==和!=操作

调用函数

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

一个函数就解决了问题,如果还要添加int类型,float类型或者其他类型,也只不过是在[]中再添加对应的类型即可,并不需要另外重写一个函数

类型推导

go编译器在调用泛型函数时会自动推导出参数类型,所以调用泛型函数时可以省略传入参数类型

调用函数

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

声明类型约束

在[]中定义了一个V的类型参数,当只有几个类型时看着还比较清晰,如果类型很多,比如float,float64,int,int64,int32等等等等,看着不是那么清晰了,所以可以声明一个类型约束,定义一个Number类型的interface,类型为int64或float64,如果有其他类型可以一直在后面加

1. 定义类型约束

type Number interface {
	int64 | float64
}

2. 使用类型约束

// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

3. 调用函数

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

总结

泛型是为了解决出现太多重复代码问题,最佳实践如下:

1. 给包含很多类型的变量声明类型约束,直接在泛型函数中使用该类型约束

2. 调用泛型函数时不必添加类型

附全文代码:

package main

import "fmt"

type Number interface {
	int64 | float64
}

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first":  34,
		"second": 12,
	}

	// Initialize a map for the float values
	floats := map[string]float64{
		"first":  35.98,
		"second": 26.99,
	}

	intsInts := map[int]int64{
	    0: 10,
		1: 20,
	}

	fmt.Printf("Non-Generic Sums: %v and %v\n",
		SumInts(ints),
		SumFloats(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 and %v\n",
		SumNumbers(ints),
		SumNumbers(floats),
        SumNumbers(intsInts))
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}
	return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}

执行结果如下: 

$ go run generic.go 
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 and 30

参考

Tutorial: Getting started with generics - The Go Programming Language

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Golang中,接口是一种接口类,可以用于处理不同类的数据。接口在编程中非常有用,因为它允许我们编写可重用、灵活的代码,而无需针对特定类进行硬编码。 在Golang中,接口可以通过使用空接口(`interface{}`)来实现。空接口是一个没有任何方法的接口,可以接受任何类的值。通过使用空接口,可以实现的功能,使得接口可以接收任何类的数据。 使用接口,我们可以在不改变接口定义的情况下,接受不同类的参数。例如,我们可以定义一个接口`Container`,用于表示一个可以容纳不同类元素的容器。这个接口可以定义添加元素、删除元素以及获取元素等方法。 使用接口的好处是可以编写灵活的代码,尽可能减少重复代码的编写。由于接口可以处理多种类的数据,我们可以将相同的逻辑应用于不同的数据类,实现代码的重用。 然而,目前Golang没有原生支持接口的功能,因此在实现接口时可能需要一些额外的代码处理。一种常见的做法是使用类断言来判断接口的实际类,然后进行相应的操作。 总而言之,虽然Golang没有内置的功能,但通过使用空接口和类断言,我们可以实现接口从而处理不同类的数据,提高代码的重用性和灵活性。 ### 回答2: Go语言是一种静态类的编程语言,其最近的版本Go 1.18中引入了接口的概念。指的是在编写代码时不指定具体类,而是允许使用者在使用时根据自己的需求来指定具体的类。 在传统的面向对象编程中,常用的接口表示方式是通过接口类断言来判断对象是否实现了某个接口。但是这种方式在处理不同类的数据时需要进行类转换,不够灵活且有一定的性能损耗。 而接口则可以在接口定义时使用类参数,通过类参数来指定接口的具体类。这样一来,在使用时就可以直接将对应类的数据传入接口中,无需进行类转换。 接口的引入为Go语言提供了更加灵活和高效的编程方式。通过接口,我们可以编写更加通用和复用的代码。它还能帮助我们更好地约束函数和数据类之间的关系,提高代码的健壮性和可读性。 不过需要注意的是,接口的引入也会带来一定的复杂性。在使用接口时,我们需要仔细考虑类参数的合理性和边界条件,并且需要充分测试以确保代码的正确性。 总之,引入接口是Go语言进一步发展的一大步。它提供了更多的编程方式,并且可以在一定程度上简化代码和提高效率。希望未来随着接口的进一步成熟和普及,我们可以看到更多高质量、灵活和通用的Go代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值