インターフェースとは
メソッドのシグネチャの集合です。Goでは暗黙的に実装されます。
// インターフェースの定義
type Speaker interface {
Speak() string
}
// 構造体
type Dog struct {
Name string
}
// Speakerインターフェースを暗黙的に実装
func (d Dog) Speak() string {
return d.Name + " がワンワン鳴いています"
}
type Cat struct {
Name string
}
func (c Cat) Speak() string {
return c.Name + " がニャーと鳴いています"
}
// インターフェースとして使用
func MakeSpeak(s Speaker) {
fmt.Println(s.Speak())
}
dog := Dog{Name: "ポチ"}
cat := Cat{Name: "タマ"}
MakeSpeak(dog) // "ポチ がワンワン鳴いています"
MakeSpeak(cat) // "タマ がニャーと鳴いています"
複数メソッドのインターフェース
type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// ポリモーフィズム
func PrintShape(s Shape) {
fmt.Printf("面積: %.2f, 周囲: %.2f\n", s.Area(), s.Perimeter())
}
PrintShape(Rectangle{10, 5}) // 面積: 50.00, 周囲: 30.00
PrintShape(Circle{7}) // 面積: 153.94, 周囲: 43.98
空インターフェース
あらゆる型を受け入れます。
// interface{} または any(Go 1.18以降)
func PrintAny(v any) {
fmt.Printf("Type: %T, Value: %v\n", v, v)
}
PrintAny(42)
PrintAny("hello")
PrintAny(true)
PrintAny([]int{1, 2, 3})
型アサーション
var i interface{} = "hello"
// 型アサーション
s := i.(string)
fmt.Println(s) // "hello"
// 安全な型アサーション
s, ok := i.(string)
if ok {
fmt.Println("文字列:", s)
}
n, ok := i.(int)
if !ok {
fmt.Println("intではありません")
}
型スイッチ
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("文字列: %s\n", v)
case bool:
fmt.Printf("真偽値: %t\n", v)
case []int:
fmt.Printf("整数スライス: %v\n", v)
default:
fmt.Printf("不明な型: %T\n", v)
}
}
インターフェースの合成
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// インターフェースを合成
type ReadWriter interface {
Reader
Writer
}
標準インターフェース
Stringer
type User struct {
Name string
Age int
}
// fmt.Stringer を実装
func (u User) String() string {
return fmt.Sprintf("%s (%d歳)", u.Name, u.Age)
}
user := User{Name: "太郎", Age: 25}
fmt.Println(user) // "太郎 (25歳)"
error
type ValidationError struct {
Field string
Message string
}
// error インターフェースを実装
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 {
return ValidationError{
Field: "age",
Message: "年齢は0以上である必要があります",
}
}
return nil
}
io.Reader と io.Writer
import (
"io"
"strings"
)
// io.Readerを受け取る
func countBytes(r io.Reader) (int, error) {
buf := make([]byte, 1024)
total := 0
for {
n, err := r.Read(buf)
total += n
if err == io.EOF {
break
}
if err != nil {
return total, err
}
}
return total, nil
}
// 使用
reader := strings.NewReader("Hello, World!")
count, _ := countBytes(reader)
fmt.Println(count) // 13
sort.Interface
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
people := []Person{
{"太郎", 30},
{"花子", 25},
{"次郎", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people) // 年齢順にソート
インターフェースのnil
type MyError struct {
Msg string
}
func (e *MyError) Error() string {
return e.Msg
}
func mayFail(fail bool) error {
var err *MyError // nil
if fail {
err = &MyError{Msg: "エラー"}
}
return err // 注意: インターフェースはnilではない
}
// 正しい方法
func mayFailCorrect(fail bool) error {
if fail {
return &MyError{Msg: "エラー"}
}
return nil
}
実践例:ロガー
type Logger interface {
Info(msg string)
Error(msg string)
}
type ConsoleLogger struct{}
func (l ConsoleLogger) Info(msg string) {
fmt.Println("[INFO]", msg)
}
func (l ConsoleLogger) Error(msg string) {
fmt.Println("[ERROR]", msg)
}
type FileLogger struct {
file *os.File
}
func NewFileLogger(path string) (*FileLogger, error) {
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
return &FileLogger{file: f}, nil
}
func (l *FileLogger) Info(msg string) {
fmt.Fprintln(l.file, "[INFO]", msg)
}
func (l *FileLogger) Error(msg string) {
fmt.Fprintln(l.file, "[ERROR]", msg)
}
// 使用(依存性注入)
type App struct {
logger Logger
}
func (a *App) Run() {
a.logger.Info("アプリケーションを開始します")
// ...
a.logger.Info("アプリケーションを終了します")
}
// 本番環境
app := &App{logger: ConsoleLogger{}}
// テスト用のモック
type MockLogger struct {
Messages []string
}
func (m *MockLogger) Info(msg string) { m.Messages = append(m.Messages, msg) }
func (m *MockLogger) Error(msg string) { m.Messages = append(m.Messages, msg) }
まとめ
- インターフェースはメソッドシグネチャの集合
- 実装は暗黙的(
implementsキーワード不要) interface{}/anyはあらゆる型を受け入れる- 型アサーションで具体的な型を取得
- 型スイッチで型による分岐
- インターフェースを合成して大きなインターフェースを作成
Stringer,error,io.Readerなどの標準インターフェース
これでGo基礎は完了です。次は実践編でファイル操作やエラー処理を学びましょう。