Goのジェネリックスの簡単な紹介

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して11日目です。クリックしてイベントの詳細をご覧ください

ジェネリック

Javaに精通している友人は、すべて泛型その使用法を理解しています。実際、Pythonは強く型付けされた言語ではないためか、ジェネリックスの役割についてはあまり明確ではありません。そのため、このセクションでは、私自身の理解に基づいて、goの内部について説明する予定泛型です。

なぜジェネリックが必要なのですか

私はこの質問に関するいくつかの情報を確認しました。StackOverFlowによって与えられた答えを見てみましょう。

ジェネリックスを使用しない場合(Pythonのリストに少し似ています)、リストに任意の要素を追加できますが、显式それを取り出すときに型を変換する必要があります。理由は次のとおりです。

List list = new ArrayList(); // 数组里面存放的是Object对象
复制代码

行くとそれはこのようなものです:

// 初始化一个存放任意类型的数组,这里的interface{} 代表任意类型
var list = make([]interface{}, 0)
复制代码

実際、配列の最初の要素を取得するジェネリックメソッドを記述したい場合、索引ジェネリック型がない場合はどうすればよいでしょうか。

func getIndex(data []interface{}, ele interface{}) int {
	for i, item := range data {
		if reflect.DeepEqual(item, ele) {
			return i
		}
	}
	return -1
}
复制代码

私たちの最初の仮定はこれであり、任意の[] interface {}を渡し、リフレクションによって2つが等しいかどうかを判断しますが、実際には、パラメーター[]intとして使用できないため、これは機能しません。[]interface{}(ねえ、私はそれを期待していなかった)

次に、別の方法でしか実行できません。

func getIndex(data interface{}, ele interface{}) int {
	switch data.(type) {
	case []int:
		result := data.([]int)
		for i, item := range result {
			if item == ele.(int) {
				return i
			}
		}
		return -1
	}
	return -1
}
复制代码

現在、私はint形式の配列のみを対象としており、配列だけでなく类型转化要素型の変換も行っています。怠惰で、ele int32、float32、float64、stringなどの型チェックサムを記述していません。他の多くの種類の変換を待っていると、これは本当に苦痛です。個人的にはこれは本当に不便だと感じています。

ジェネリック医薬品の世界に入る

goは2.xでジェネリックを公式にサポートすると思いましたが、後で1.18のベータ版がすでに基本的にジェネリックをサポートしていることがわかりました。少し前に、goの公式ウェブサイトでgo1.18が正式にリリースされていることを発見したブロガーは、それをダウンロードして試してみました。ただし、現時点では、ジェネリックスに遭遇すると、Golandエディターにいくつかのバグが発生します。これは、Goland2022.1バージョンからのみ予想されます。

ジェネリックメソッドの記述

go里面能用到泛型的地方有好几处,我们今天就只讲讲简单点的,运用于方法和接口上的泛型。先来看看定义在方法上的泛型,我们来改造刚才的例子:

func findIndex[T comparable](data []T, ele T) int {
	for i, item := range data {
		if item == ele {
			return i
		}
	}
	return -1
}
复制代码

我们可以看到,在函数名和参数之间多了个[T comparable]的语句,这个的意思是,声明这个方法接收的类型为comparable,并定义为T,接着参数为T(comparable)的数组,和T。

有的同学要问了,为啥不是:

func findIndex(data []comparable, ele comparable) int {
	for i, item := range data {
		if item == ele {
			return i
		}
	}
	return -1
}
复制代码

问的很好,首先这样会与我们上面写的demo一样,[]int不为[]comparable类型,其次,这里的T,也可以看做一个变量,我的理解是这样的,我们以往定义的方法,参数都是固定的,现在我想让参数的类型也能随心所欲地变化。但是这个变化又有个度,不是所有类型都可以,所以我界定了一个T类型,也就是comparable,可比较的(comparable是go原生的方法,int,float等类型都默认实现了这个接口)。所以你传int的时候,我发现是可比较的,这时候T就等于int,传入float32的时候,T这时候就是float32。所以我个人感觉,可以把T当做变化的类型

编写泛型接口

泛型接口比较简单了,简单的说就算是类型的结合,比如我这个方法,获取索引,我只想要支持string,float和int,其他的我都不支持。

type MyType interface {
  int | float32 | string
}
复制代码

有人说,我的time.Duration这种呢?是属于int64的扩展类型:

如果也要支持这种的话,我们在类型前面加上~即可:

type MyType interface {
  int | float32 | string | ~int64
}
复制代码

这样既可以支持int64,也可以支持time.Duration这种扩展类型。

二者结合

我们刚才改造的例子,传入的参数我们要从comparable改为MyType,看一个完整的例子:

package main

import "fmt"

type MyType interface {
	~int | ~float32 | ~int64 | string
}

func findIndex[T MyType](data []T, ele T) int {
	for i, item := range data {
		if item == ele {
			return i
		}
	}
	return -1
}

func main() {
	arr1 := []int{2, 3, 4, 5, 6}
	arr2 := []string{"李逍遥", "吴敬梓", "头铁娃", "陈玄风", "巴基斯坦"}
	four := findIndex(arr1, 4)
	iron := findIndex(arr2, "头铁娃")
	fmt.Println("4的索引是", four)
	fmt.Println("头铁娃的索引是", iron)
}

复制代码

看看执行结果:

WeChatpicture_20220411222937.png

我们测试下换个我们没有的类型: uint


要約すると、ジェネリック医薬品の役割はまだかなり大きいです。[] interface {}を書いてみたところ、ジェネリックスがないことがいかに不快であるかがわかりました。mdniceのピクチャーベッドは少し引き伸ばされており(解像度が非常に大きい)、後でエディターを変更する予定です。今日のコンテンツはここで共有されています、日本の肝臓はあまりにも不快です!

おすすめ

転載: juejin.im/post/7085353484354060302