8.4 ジェネリクスとインタフェース
使える型の範囲を限定する型制約としては、anyやcomparable以外にも任意のインタフェースが指定できる
例えば、空間に置かれる点の距離などに関する演算の場合
まず、型パラメータを使って「同じ型を持つ2つの値を保持する」・「fmt.Stringerを実装する」を満たす構造体を定義する
code:go
// Tはfmt.StringerインタフェースのString()メソッドをもつ構造体
Val1 T
Val2 T
}
次に型パラメータを使って「fmt.Stringerを埋め込んでいる」・「別の同じ型の値と比較し、float64を返すDiffメソッドを持つ」のインタフェースを定義する
code:go
type DifferT any interface { fmt.Stringer // fmt.Stringerを埋め込むことでPairの制約を伝搬させる
Diff(T) float64
}
上記2つの型を使用しペア間の距離が近いPairを返すFindCloser関数を定義する
code:go
// インタフェースDifferを満たす型Tを型パラメータとして指定
func FindCloser[T DifferT](pair1, pair2 PairT) PairT { d1 := pair1.Val1.Diff(pair1.Val2) // pair1の距離
d2 := pair2.Val1.Diff(pair2.Val2) // pair2の距離
if d1 < d2 { // pair1のほうがpair2よりも距離が近い
return pair1
}
return pair2 // pair2のほうがpair1よりも近いか同じ距離
}
実際に使用する
2D
code:go
// 型Point2Dの定義
type Point2D struct { // 2次元の点
X, Y int // X座標、Y座標
}
// Point2Dに付随するメソッドの定義
// 1. メソッドString -- 自分の(2次元の)「座標」を出力
func (p2 Point2D) String() string {
return fmt.Sprintf("%d,%d", p2.X, p2.Y) }
// 2. メソッドDiff -- 引数で渡された2次元の点と自分との距離を計算
func (p2 Point2D) Diff(from Point2D) float64 {
x := p2.X - from.X
y := p2.Y - from.Y
return math.Sqrt(float64(x*x) + float64(y*y)) // 2次元距離
}
func main() {
fmt.Println("=== 2Dバージョン ===")
pair2Da := PairPoint2D{Point2D{1, 1}, Point2D{5, 5}} fmt.Println("1つ目のペア:", pair2Da) // {1,1 5,5} pair2Db := PairPoint2D{Point2D{10, 10}, Point2D{15, 5}} fmt.Println("2つ目のペア:", pair2Db) // {10,10 15,5} closer := FindCloser(pair2Da, pair2Db)
fmt.Println("距離が近いペア:", closer) // {1,1 5,5} }
3D
code:go
// 型Point3Dの定義
type Point3D struct { // 3次元の点
X, Y, Z int // X座標、Y座標、Z座標
}
// Point3Dに付随するメソッドの定義
// 1. メソッドString -- 自分の(3次元の)「座標」を出力
func (p3 Point3D) String() string {
return fmt.Sprintf("%d,%d,%d", p3.X, p3.Y, p3.Z) }
// 2. メソッドDiff -- 引数で渡された3次元の点と自分との距離を計算
func (p3 Point3D) Diff(from Point3D) float64 {
x := p3.X - from.X
y := p3.Y - from.Y
z := p3.Z - from.Z
return math.Sqrt(float64(x*x) + float64(y*y) + float64(z*z)) // 3次元距離
}
func main() {
fmt.Println("=== 3Dバージョン ===")
pair3Da := PairPoint3D{Point3D{1, 1, 10}, Point3D{5, 5, 0}} pair3Db := PairPoint3D{Point3D{10, 10, 10}, Point3D{11, 5, 0}} closer2 := FindCloser(pair3Da, pair3Db)
}
コンパイル時エラー
code:go
type StringerString string // メソッドStringをもつstring
// StringerStringに付随するメソッドの定義
// インタフェースDifferは満たされていない
// 1. メソッドString
func (ss StringerString) String() string {
return string(ss)
}
func main() {
// 同じ型のペアが指定されていない場合、コンパイル時エラー
pair2Da := PairPoint2D{Point2D{1, 1}, Point2D{105, 5}} pair3Da := PairPoint3D{Point3D{1, 1, 10}, Point3D{5, 5, 0}} closer3 := FindCloser(pair2Da, pair3Da)
// ./prog.go:12:33: in call to FindCloser, type PairPoint3D of pair3Da does not match inferred type PairPoint2D for PairT // Differを実装していない場合、コンパイル時エラー
// ./prog.go:9:23: in call to FindCloser, T (type StringerString) does not satisfy DifferT (missing method Diff) }