こんにちは。Tomoyuki(@tomoyuki65)です。
Go言語(Golang)には未知の型を扱う方法として、全ての型を受け取れる空インターフェース型(interface{})と、Goのバージョン1.18から導入されたジェネリクスという機能があります。
この記事では、そんな空インターフェース型とジェネリクスについて解説します。
Go言語(Golang)の空インターフェース型(interface{})とジェネリクスで未知の型を扱う方法
空インターフェース型(interface{})
まず空インターフェース型(interface{})については、以下のように型アサーション(型の種類を特定)をすることで未知の型でも扱えます。
package main
import (
"fmt"
)
// ログ出力関数
func outputLog(p interface{}) {
// 型アサーション(型の種類を特定)が必要
switch value := p.(type) {
case int:
fmt.Println("int型の値:", value)
case string:
fmt.Println("string型の値:", value)
case bool:
fmt.Println("bool型の値:", value)
default:
fmt.Println("パラメータにはint型またはstring型またはbool型の値を入力して下さい。")
}
}
func main() {
// パラメータにint型の値を入力
outputLog(10)
// パラメータにstring型の値を入力
outputLog("テキスト")
// パラメータにbool型の値を入力
outputLog(true)
// パラメータにfloat64型の値を入力
outputLog(1.25)
}
実行結果
int型の値: 10
string型の値: テキスト
bool型の値: true
パラメータにはint型またはstring型またはbool型の値を入力して下さい。
このように柔軟性を持たせるために未知の型を扱いたい場合は空インターフェース型(interface{})を使うことで実現可能です。
ただし、型チェックはコンパイル時ではなく実行時に行われるので型安全性が犠牲になることや、型アサーションが必要で実行時のパフォーマンスに影響を与える可能性がある点はご注意下さい。
※尚、interface{}の別名として「any」を使うこともできます。
ジェネリクス
次にGoのバージョン1.18からはジェネリクスという機能が実装され、型パラメータ(複数の型に対して動作する型のパラメータ)を使用してデータ型に依存しない再利用可能なコードが記述できるようになりました。
例えばジェネリクスを使うと、以下のようにパラメータに複数の型を指定できます。
package main
import (
"fmt"
)
// ジェネリクスを使ったログ出力関数
func outputLog[T int | string | bool](p T) {
fmt.Println("パラメータの値:", p)
}
func main() {
// パラメータにint型の値を入力
outputLog(10)
// パラメータにstring型の値を入力
outputLog("テキスト")
// パラメータにbool型の値を入力
outputLog(true)
}
※型パラメータに指定していない型の値を関数のパラメータに渡すとコンパイルエラーになる
実行結果
パラメータの値: 10
パラメータの値: テキスト
パラメータの値: true
また、型パラメータを使うと関数実行時に型を指定できるようになるため、以下のようにすることも可能です。
package main
import (
"fmt"
)
// ジェネリクスを使ったログ出力関数
func outputLog[T int | string | bool](p T) {
fmt.Println("パラメータの値:", p)
}
// 型パラメータにany(interface{})を使った場合
func outputLog2[T any](p T) {
fmt.Println("パラメータの値:", p)
}
func main() {
// 型パラメータにint型を指定し、パラメータにint型の値を入力
outputLog[int](10)
// 型パラメータにstring型を指定し、パラメータにstring型の値を入力
outputLog[string]("テキスト")
// 型パラメータにbool型を指定し、パラメータにbool型の値を入力
outputLog[bool](true)
// 型パラメータにfloat64型を指定し、パラメータにfloat64型の値を入力
outputLog2[float64](1.13)
}
実行結果
パラメータの値: 10
パラメータの値: テキスト
パラメータの値: true
パラメータの値: 1.13
このようにジェネリクスを使うことでも未知の型を扱うことができますが、空インターフェース型(interface{})とは違ってコンパイル時に型チェックが行われるため、型安全性も問題ありません。
そのため、Goのバージョン1.18以上を使っているならまずジェネリクスを使うべきですが、柔軟性については空インターフェース型(interface{})の方が高いため、特徴を理解してしっかり使い分けるのが大事になります。
最後に
今回はGo言語(Golang)の空インターフェース型(interface{})とジェネリクスについて解説しました。
Go言語で未知の型を扱いたい場合は空インターフェース型(interface{})とジェネリクスを使うことになるので、使い方や特徴についてしっかり理解しておきましょう!
コメント