1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ここはinterfaceだけで良くない??って思ったけど、genericsも併用して良かった話

Last updated at Posted at 2023-05-28

はじめに

なんかすごいタイトル長くなってしまいましたが、タイトルのままの記事です。
「genericsを使ってみたいと思って、いざ使ったらinterfaceだけで解決できそう」と思ったら実はgenericsが有効だったという経験をしたので記事にしてみました。
どんな場面なのかで有効だったのか、サクッとみていこうと思います!

結論interfaceのsliceを使った処理に有効

サンプルコードを作ってみたので、少しみていきます!

main.go
package main

import "fmt"

type Product interface {
	GetID() int
}

// ClockはProductを実装
type Clock struct {
	ID int
}

func (c *Clock) GetID() int {
	return c.ID
}

// TableはProductを実装
type Table struct {
	ID int
}

func (t *Table) GetID() int {
	return t.ID
}

// Productの重複判定処理
func isDuplicated[T Product](t []T) bool {
	dupMap := map[int]struct{}{}
	for _, v := range t {
		if _, ok := dupMap[v.GetID()]; ok {
			return true
		}
		dupMap[v.GetID()] = struct{}{}
	}
	return false
}

func main() {
	if isDuplicated(clocks) {
		fmt.Println("clockは重複しています")
	}
	if isDuplicated(tables) {
		fmt.Println("tableは重複しています")
	}
}

var clocks = []*Clock{
	{ID: 1},
	{ID: 1},
	{ID: 3},
}

var tables = []*Table{
	{ID: 1},
	{ID: 2},
	{ID: 3},
}

Productが重複しているのか判定することが目的のコードですね。
ClockとTableはどちらもProduct interfaceを実装しているので、最終的にはClockやTableが重複しているかチェックすることを目指しています。

idDuplicated関数について、genericsを用いて引数にはProductのsliceがくるようにしています。
コードは以下の部分ですね。

func isDuplicated[T Product](t []T) bool {
	dupMap := map[int]struct{}{}
	for _, v := range t {
		if _, ok := dupMap[v.GetID()]; ok {
			return true
		}
		dupMap[v.GetID()] = struct{}{}
	}
	return false
}

この部分について、

func isDuplicated(t []Product) bool

のようにInterfaceのsliceを引数の型にして、実装しても問題ないように思えます。ていうか僕は思っていました。

ただこうしてしまうと以下のようなコンパイルエラーが発生してしまいます 。

cannot use clocks (variable of type []*Clock) as type []Product in argument to isDuplicated
cannot use tables (variable of type []*Table) as type []Product in argument to isDuplicated

*Clock*TableはProductを実装しているのに、sliceになると異なる型を判定されてしまいます。
genericsではこの問題を解決することができます。
func isDuplicated[T Product](t []T) boolの部分で引数にclocksを入れると、[]Tは[]*Clockと判定されてコンパイルしてくれるみたいですね!

<補足>
以下のようにProductのsliceにClockやTableをappendで入れていく分には問題ありません。

pros := []Product{}
pros = append(pros, &Clock{
    ID: 1,
})
pros = append(pros, &Table{
    ID: 1,
})

おわりに

今回はinterfaceのsliceについて、genericsを使ってうまく処理する方法を見てきました。
もしinterfaceのsliceの扱いに困っていた方がいらっしゃったらぜひこの方法も検討してみてください!
最後まで読んでいただきありがとうございました!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?