# 2026年のGo言語インターフェース完全攻略:合成、型アサーション、面接対策 > Go 1.26の自己参照ジェネリクスを含む、インターフェース合成と型アサーションを徹底解説。実践的なコード例と技術面接でよく聞かれる質問も網羅。 - Published: 2026-09-10 - Updated: 2026-09-10 - Author: Anthony Fillion-Maillet - Tags: go, interfaces, type-assertions, generics, go-1.26 - Reading time: 9 min --- Goのインターフェースは、実装を規定せずに振る舞いを定義することで、柔軟でテストしやすいコードの核となります。Go 1.26では、自己参照ジェネリクス、新しいリフレクションイテレータ、`errors.AsType`による型安全なエラー処理が追加されました。本記事では、インターフェース合成、型アサーション、埋め込みパターン、そして技術面接で問われる質問について解説します。 > **Go 1.26 インターフェースの強化点** > > ジェネリック型が型パラメータリスト内で自身を参照できるようになりました:`type Adder[A Adder[A]] interface { Add(A) A }`。このF境界量化パターンにより、戻り値の型を実装型自身に制約するインターフェースが実現できます。 ## インターフェース合成と埋め込みパターン インターフェースの埋め込みは、複数のインターフェースを単一の契約に結合します。結果として、埋め込まれたすべてのインターフェースのメソッドを要求する新しいインターフェースが生成されます。このパターンは重複を避け、明確な抽象化の境界を作成します。 ```go // interfaces.go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error } // Composed interface embedding three interfaces type ReadWriteCloser interface { Reader Writer Closer } ``` `Read`、`Write`、`Close`メソッドを実装する任意の型は、自動的に`ReadWriteCloser`を満たします。[Go標準ライブラリ](https://pkg.go.dev/io)の`io`パッケージでは、このパターンが広く使用されています。 面接でよく聞かれる質問として、「なぜGoは小さく焦点を絞ったインターフェースを好むのか」というものがあります。その答えは合成可能性にあります:`io.Reader`は、たった1つのメソッドを要求するだけで、数百の関数で使用されています。大きなインターフェースは結合を強くし、再利用性を低下させます。 ## 型アサーション:構文と安全性 型アサーションは、インターフェース値から具象型を抽出します。2値形式を使用することで、成功を示すブール値を返し、パニックを防ぎます。 ```go // assertions.go func processValue(v interface{}) { // Two-value assertion: safe, no panic if str, ok := v.(string); ok { fmt.Printf("String value: %s\n", str) return } // Type switch for multiple types switch val := v.(type) { case int: fmt.Printf("Integer: %d\n", val) case float64: fmt.Printf("Float: %.2f\n", val) case []byte: fmt.Printf("Bytes: %x\n", val) default: fmt.Printf("Unknown type: %T\n", val) } } ``` 型スイッチは、複数の可能な型をきれいに処理します。各ケースでは`val`がそのブロック内でアサートされた型に束縛されるため、個別のアサーションは不要です。 > **パニックのリスク** > > `str := v.(string)`のような単一値アサーションは、アサーションが失敗するとパニックします。本番コードでは常に2値形式または型スイッチを使用してください。 面接では型アサーションのパフォーマンスについて質問されることがあります。ランタイムは型記述子の単一の比較を行うため、アサーションは低コストです。大きな構造体へのポインタをラップするインターフェース値では間接参照のためコストが増加しますが、アサーション自体はO(1)のままです。 ## Go 1.26の自己参照ジェネリクス Go 1.26では自己参照型パラメータが導入され、メソッドが実装型を返す必要があるインターフェースが可能になりました。このパターンは[F境界量化](https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification)と呼ばれ、長年の制限を解決しています。 ```go // builder.go // Self-referential interface: methods return the same type type Builder[B Builder[B]] interface { WithName(name string) B WithAge(age int) B Build() string } type PersonBuilder struct { name string age int } func (p PersonBuilder) WithName(name string) PersonBuilder { p.name = name return p } func (p PersonBuilder) WithAge(age int) PersonBuilder { p.age = age return p } func (p PersonBuilder) Build() string { return fmt.Sprintf("%s, %d years old", p.name, p.age) } // Generic function using the self-referential constraint func configure[B Builder[B]](b B, name string, age int) string { return b.WithName(name).WithAge(age).Build() } ``` 制約`Builder[B]`は、`WithName`と`WithAge`がジェネリックな`Builder`ではなく`B`を返すことを保証します。これがなければ、戻り値の型はインターフェースとなり、具象型の情報が失われ、メソッドチェーンが壊れてしまいます。 数学型もこのパターンの恩恵を受けます。`Addable[A Addable[A]]`インターフェースは、`Add(A) A`が同じ数値型を返すことを保証し、`BigInt`と`Decimal`の値の誤った混在を防ぎます。 ## errors.AsType:型安全なエラーアンラップ Go 1.26では[`errors.AsType`](https://pkg.go.dev/errors#AsType)が追加され、`errors.As`のポインタを使用したパターンが置き換えられました。ジェネリック版はアンラップされたエラーを直接返します。 ```go // errors_handling.go type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message) } func handleError(err error) { // Go 1.26: generic type-safe unwrapping if valErr, ok := errors.AsType[*ValidationError](err); ok { log.Printf("Validation error on field %s\n", valErr.Field) return } // Before Go 1.26: pointer-based unwrapping // var valErr *ValidationError // if errors.As(err, &valErr) { ... } log.Printf("Unexpected error: %v\n", err) } ``` 新しいAPIは、別々の変数宣言を不要にし、関数呼び出しでターゲット型を明示的にします。エラーチェーンは以前と同じ方法で検索されます。変わったのはインターフェースだけです。 ## インターフェース検査のためのリフレクションイテレータ Go 1.26では`reflect`パッケージにイテレータメソッドが追加されました。`Type.Methods()`と`Value.Methods()`はメソッドを反復処理するためのイテレータを返し、インデックスベースのループを置き換えます。 ```go // reflection.go import "reflect" func inspectInterface(v interface{}) { t := reflect.TypeOf(v) // Go 1.26: iterator-based method inspection fmt.Printf("Type %s methods:\n", t.Name()) for method := range t.Methods() { fmt.Printf(" %s: %s\n", method.Name, method.Type) } // For struct fields (also new in 1.26) if t.Kind() == reflect.Struct { for field := range t.Fields() { fmt.Printf(" Field: %s (%s)\n", field.Name, field.Type) } } } ``` イテレータパターンはGo 1.23のrange-over-function機能と整合しています。コードはより読みやすくなり、パフォーマンスのペナルティはありません:イテレータは遅延評価で値を生成します。 ## コンパイル時のインターフェース満足チェック Goは、具象値をインターフェース変数に代入する際にコンパイル時にインターフェース満足をチェックします。空白識別子代入を使用した明示的なチェックにより、大規模なコードベースでエラーを早期に検出できます。 ```go // compile_check.go type Storage interface { Save(key string, data []byte) error Load(key string) ([]byte, error) Delete(key string) error } type FileStorage struct { basePath string } // Compile-time check: fails if FileStorage misses a method var _ Storage = (*FileStorage)(nil) func (f *FileStorage) Save(key string, data []byte) error { path := filepath.Join(f.basePath, key) return os.WriteFile(path, data, 0644) } func (f *FileStorage) Load(key string) ([]byte, error) { path := filepath.Join(f.basePath, key) return os.ReadFile(path) } func (f *FileStorage) Delete(key string) error { return os.Remove(filepath.Join(f.basePath, key)) } ``` `var _ Storage = (*FileStorage)(nil)`という行は、`*FileStorage`が`Storage`を満たす場合にのみコンパイルされます。このパターンは、値が代入される実行時ではなく、欠落しているメソッドを即座に検出します。 ## 空インターフェースと型制約 空インターフェース`interface{}`は任意の値を受け入れ、`any`はGo 1.18以降そのエイリアスです。ジェネリック制約は、実行時アサーションなしでコンパイル時の型安全性を提供します。 ```go // constraints.go import "golang.org/x/exp/constraints" // Generic function with numeric constraint func Sum[T constraints.Integer | constraints.Float](values []T) T { var total T for _, v := range values { total += v } return total } // Comparable constraint for map keys func Contains[K comparable, V any](m map[K]V, key K) bool { _, exists := m[key] return exists } ``` `interface{}`よりもジェネリクスを優先することで、型アサーションが不要になり、コンパイル時に型の不一致を検出できます。トレードオフは関数シグネチャの複雑さが増すことなので、ジェネリクスは関数が本当に複数の型を操作する場合に最適です。 ## Goインターフェースに関する技術面接の質問 面接官は複数のレベルでインターフェースの知識をテストします。以下は一般的な質問と簡潔な回答です。 **Q:nilインターフェース値でメソッドを呼び出す場合と、インターフェース内のnil具象値でメソッドを呼び出す場合、何が起こりますか?** nilインターフェースは型も値も持たないため、任意のメソッドを呼び出すとパニックします。nilポインタを保持する非nilインターフェースは型を持っているため、メソッドはnilレシーバで実行されます。この動作により、`(*bytes.Buffer)(nil).String()`が空文字列を返すようなパターンが可能になります。 **Q:インターフェースの比較はどのように機能しますか?** 2つのインターフェース値は、同じ動的型と等しい動的値を持つ場合に等しくなります。比較不可能な型(スライス、マップ、関数)を持つインターフェースを比較すると、実行時にパニックします。 **Q:メソッドはポインタレシーバと値レシーバのどちらを使用すべきですか?** ポインタレシーバは変更を可能にし、大きな構造体のコピーを回避します。値レシーバは並行使用に安全で、値とポインタの両方で動作します。いずれかのメソッドがポインタレシーバを必要とする場合、一貫性のためにその型のすべてのメソッドがポインタレシーバを使用すべきです。なぜなら、ポインタレシーバメソッドを必要とするインターフェースを満たすのは`*T`だけだからです。 ```go // receiver_rules.go type Counter struct { count int } // Pointer receiver: modifies the struct func (c *Counter) Increment() { c.count++ } // Value receiver: read-only operation func (c Counter) Value() int { return c.count } type Incrementer interface { Increment() } func main() { var c Counter // var i Incrementer = c // Compile error: Counter lacks Increment var i Incrementer = &c // OK: *Counter has Increment i.Increment() } ``` **Q:「インターフェースを受け入れ、構造体を返す」原則を説明してください。** インターフェースを受け入れる関数は具象実装から分離され、モックによるテストが可能になります。具象型を返すことで、呼び出し側は型アサーションなしでその型のすべてのメソッドにアクセスできます。インターフェースを返すと、将来のメソッド追加が型アサーションの背後に隠されてしまいます。 Goの面接対策については、[並行性パターンの面接質問](/technologies/go/interview-questions/concurrency-patterns)モジュールや[テストモジュール](/technologies/go/interview-questions/testing)も参照してください。 > **面接のコツ** > > インターフェースの設計を求められた場合、まず単一メソッドのケースから始めてください。複数のメソッドが常に一緒に呼び出される場合にのみ拡張します。標準ライブラリの`Stringer`、`Reader`、`Handler`インターフェースは、それぞれ1つのメソッドを定義しています。 ## シニアGo開発者がインターフェースについて知っておくべきこと - 埋め込みによるインターフェース合成は、継承階層なしで柔軟な契約を作成します。インターフェースは小さく保ちましょう:1〜3メソッドでほとんどのユースケースをカバーできます。 - 型アサーションと型スイッチは具象型を安全に抽出します。パニックを避けるため、本番コードでは常に2値形式またはスイッチを使用してください。 - Go 1.26の自己参照ジェネリクスは、メソッドが実装型を返す必要があるビルダーパターンや数学型を可能にします。 - `errors.AsType`はジェネリックで型安全なAPIでエラーアンラップを簡素化します。Go 1.26以降をターゲットとする新しいコードで使用してください。 - `var _ Interface = (*Type)(nil)`によるコンパイル時インターフェースチェックは、テスト実行前に欠落メソッドを検出します。 - コンパイル時の型安全性がシグネチャの複雑さを上回る場合、`interface{}`よりもジェネリクスを優先してください。 - 「インターフェースを受け入れ、構造体を返す」ガイドラインは、呼び出し側に柔軟性を維持しながら完全な機能を公開します。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/go/go-interfaces-advanced-composition-type-assertions-2026