fmt.Stringer インターフェース
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 75/107。
fmt.Stringer インターフェースは、型をプリント(出力)した際の表示方法を制御します。これは Go で最も一般的に実装されているインターフェースの1つで、fmt パッケージで定義されています。
type Stringer interface {
String() string
}値を fmt.Println、%v を指定した fmt.Printf、または同様の関数に渡すと、Goはその型が Stringer を実装しているかどうかを確認します。実装している場合、Goは String() メソッドを呼び出してテキスト表現を取得します。
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}String() メソッドがない場合、出力にはデフォルトの構造体フォーマット {23.5} が表示されます。Stringer を実装することで、型がどのように表示されるかを正確に制御できます。
これは、生のフィールド値が意味を明確に伝えない型において特に便利です。Duration 型は、生のナノ秒を表示する代わりに "2h 30m" と表示されるかもしれません。また、User 型は、すべてのフィールドを表示する代わりに、フォーマットされた名前を表示するかもしれません。
チャレンジ
簡単時間を人間が読みやすい形式で表示するタイムトラッキングシステムを構築しましょう!fmt.Stringer インターフェースを実装して、カスタム型がプリントされたときにどのように表示されるかを制御します。
コードは2つのファイルに分けて構成します:
duration.go: カスタムの文字列表現を持つ時間関連の型を定義します。合計分数を整数として保持する
Duration構造体を作成します。String()メソッドを実装し、プリントされたときに、150分なら2h 30m、45分なら0h 45mのように読みやすい形式で表示されるようにします。時間(hours)と分(minutes)は常に表示される必要があります。Name(string) とTime(Duration) のフィールドを持つTask構造体を作成します。Task にString()を実装し、[Name]: [duration]という形式で表示されるようにします。ここで、duration はそれ自身の String メソッドを使用します。main.go: タスクとその期間を構築して表示します。タスクの数を読み取り、次に各タスクについてその名前と分単位の期間を読み取ります。Task インスタンスを作成し、
fmt.Printlnを使用してそれぞれを直接プリントします。Stringer インターフェースが自動的にフォーマットを処理します。すべてのタスクをプリントした後、全タスクの合計時間を計算し、以下の形式でプリントします:
Total: [duration]
以下の入力が提供されます:
- 1行目:タスクの数
- 続く行:タスク名と分数(1タスクにつき2行)
例えば、以下が与えられた場合:
3
Code Review
90
Meeting
45
Documentation
120出力は以下のようになります:
Code Review: 1h 30m
Meeting: 0h 45m
Documentation: 2h 0m
Total: 4h 15mまた、以下が与えられた場合:
2
Design
180
Testing
65出力は以下のようになります:
Design: 3h 0m
Testing: 1h 5m
Total: 4h 5mStringer を実装することで、作成した型を Go のプリント関数とシームレスに統合できます。{90} のような生の構造体フィールドが表示される代わりに、ユーザーにとって意味のある出力を得ることができます。
チートシート
fmt.Stringer インターフェースは、出力時の型の表示方法を制御します:
type Stringer interface {
String() string
}fmt.Println や fmt.Printf に %v を指定して値を渡すと、Go はその型が Stringer を実装しているかどうかを確認します。実装されている場合、Go は String() メソッドを呼び出してテキスト表現を取得します。
実装例:
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}String() メソッドがない場合、出力にはデフォルトの構造体フォーマットである {23.5} が表示されます。Stringer を実装することで、型がどのように表示されるかを正確に制御でき、出力をより意味のある、ユーザーフレンドリーなものにすることができます。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// タスクの数を読み込む
line, _ := reader.ReadString('\n')
numTasks, _ := strconv.Atoi(strings.TrimSpace(line))
// TODO: タスクを格納するためのスライスを作成する
// TODO: 合計時間を追跡する
for i := 0; i < numTasks; i++ {
// タスク名を読み込む
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// タスクの所要時間(分)を読み込む
minutesLine, _ := reader.ReadString('\n')
minutes, _ := strconv.Atoi(strings.TrimSpace(minutesLine))
// TODO: 名前と所要時間を使用してTaskを作成する
// TODO: タスクを表示する(fmt.PrintlnはStringerインターフェースを使用します)
// TODO: 合計時間に分を加算する
_ = name
_ = minutes
}
// TODO: 合計時間を "Total: Xh Ym" の形式で表示する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー11標準ライブラリとOOP
io.Reader と io.Writersort.Interfacefmt.Stringer インターフェースencoding/json と構造体http.Handler インターフェースまとめ:REST API モデル