Menu
Coddy logo textTech

空のインターフェース (any)

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 28/107。

空のインターフェース (empty interface) は、メソッドを一つも持たないインターフェースです。Goのすべての型は少なくとも0個のメソッドを持っているため、すべての型は自動的に空のインターフェースを満たします。これにより、あらゆる型の値を保持することが可能になります。

var anything interface{}

anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}

Go 1.18から、キーワード anyinterface{} のエイリアスとして導入されました。これらは完全に互換性がありますが、any の方が読みやすくなっています。

var value any = "Go is awesome"

空のインターフェースは、未知の型や多様な型の値を扱う必要がある場合に便利です。fmt.Println のような関数は、これを使用してあらゆる型の引数を受け取ります:

func PrintAnything(v any) {
    fmt.Println("Value:", v)
}

func main() {
    PrintAnything(100)
    PrintAnything("text")
    PrintAnything(3.14)
}

しかし、トレードオフがあります。空のインターフェースに値が格納されると、その具体的な型情報へのアクセスが失われます。

基底の型を最初に取り出すことなしに、メソッドを直接呼び出したりフィールドにアクセスしたりすることはできません。次のレッスンでは、型アサーション(type assertions)や型スイッチ(type switches)を使用して、空のインターフェースから具体的な型を取得する方法について説明します。

challenge icon

チャレンジ

簡単

空のインターフェースを使用して、あらゆる型の値を保持できる柔軟なストレージコンテナを作成しましょう。any を使用することで、同じ構造体の中に異なる型を格納できることを示すシンプルなボックスを作成します。

コードは2つのファイルに分けて構成します:

  • box.go: any 型の単一フィールド Content を持つ Box 構造体を定義します。任意の型の値を受け取り、その値を含む Box へのポインタを返す NewBox コンストラクタ関数を作成します。ボックスに何が格納されているかを示す文字列を返す Describe メソッドを追加します。
  • main.go: 入力から異なる値を読み取り、整数、文字列、およびブール値を含むボックスを作成し、各ボックスの説明を出力して、同じ Box 型がどのようにして完全に異なる値を保持できるかを示します。

以下の入力が提供されます:

  • 1行目: 整数値
  • 2行目: 文字列値
  • 3行目: ブール値 (true または false)

Describe メソッドは、以下の形式の文字列を返す必要があります:

Box contains: [value]

例えば、42hellotrue が与えられた場合、出力は以下のようになります:

Box contains: 42
Box contains: hello
Box contains: true

ここでの重要なポイントは、単一の Box 構造体が整数、文字列、またはブール値を格納できるということです。これはすべて any 型があらゆる型の値を受け入れるためです。各ボックスは異なるものを保持していますが、それらはすべて同じ Box 型です。

チートシート

空のインターフェース (interface{}) は、メソッドを1つも持たないインターフェースです。Goのすべての型は少なくとも0個のメソッドを持っているため、すべての型は自動的に空のインターフェースを満たします。これにより、あらゆる型の値を保持することができます。

var anything interface{}

anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}

Go 1.18から、interface{} のエイリアスとして any キーワードが導入されました。これらは完全に互換性がありますが、any の方が読みやすくなっています:

var value any = "Go is awesome"

空のインターフェースは、未知の型や多様な型の値を扱うのに便利です。関数は任意の型の引数を受け取ることができます:

func PrintAnything(v any) {
    fmt.Println("Value:", v)
}

func main() {
    PrintAnything(100)
    PrintAnything("text")
    PrintAnything(3.14)
}

トレードオフ: 値が空のインターフェースに格納されると、その具体的な型情報へのアクセスが失われます。型アサーションや型スイッチを使用して元の型を取り出さない限り、メソッドを直接呼び出したりフィールドにアクセスしたりすることはできません。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 整数値を読み込む
	line1, _ := reader.ReadString('\n')
	intVal, _ := strconv.Atoi(strings.TrimSpace(line1))

	// 文字列値を読み込む
	line2, _ := reader.ReadString('\n')
	strVal := strings.TrimSpace(line2)

	// 論理値を読み込む
	line3, _ := reader.ReadString('\n')
	boolVal, _ := strconv.ParseBool(strings.TrimSpace(line3))

	// TODO: NewBox を使用して各値のボックスを作成する
	// TODO: Describe メソッドを使用して各ボックスの説明を出力する

	// コンパイルエラーを避けるために変数を使用する(実装時にこれらの行を削除してください)
	_ = intVal
	_ = strVal
	_ = boolVal

	// 出力形式の例:
	// fmt.Println(box.Describe())
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン