コンテンツにスキップ

Go/type

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

typeキーワードはGoプログラミングにおいて中心的な役割を果たし、さまざまな状況や他のキーワードとの組み合わせで使用されます。この総合ガイドでは、typeキーワードのすべての用途と他のキーワードとの組み合わせについて詳しく説明します。

1. 基本的な型定義

[編集]

1.1 カスタム型の作成

[編集]
type UserID int
type Temperature float64
type Status string

基本的な使い方として、既存の型から新しい型を作成します。これらの型は元の型と互換性がありません。

1.2 型エイリアス (Go 1.9以降)

[編集]
type OldName = NewName
type ByteSlice = []byte
type UserMap = map[string]User

=記号を使用すると、既存の型の別名(エイリアス)を作成します。これは互換性を保ちながら型名を変更する場合に便利です。

2. type と struct キーワードの組み合わせ

[編集]

2.1 基本的な構造体定義

[編集]
type Person struct {
    Name    string
    Age     int
    Address string
}

structキーワードと組み合わせて複合データ型を定義します。

2.2 タグ付き構造体フィールド

[編集]
type Product struct {
    ID          int       `json:"id" db:"product_id"`
    Name        string    `json:"name" validate:"required"`
    Price       float64   `json:"price" validate:"gt=0"`
    CreatedAt   time.Time `json:"created_at" db:"created_at"`
}

構造体フィールドに「バッククォート」でタグを追加して、JSONシリアル化やバリデーションなどのメタデータを提供できます。

2.3 埋め込みフィールド

[編集]
type Animal struct {
    Species string
    Age     int
}

type Pet struct {
    Animal        // 型名のみ(匿名フィールド)
    Name   string
    Owner  string
}

構造体内に型名のみを記述することで、その型を埋め込むことができます。

3. type と interface キーワードの組み合わせ

[編集]

3.1 基本的なインターフェース定義

[編集]
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

interfaceキーワードと組み合わせて、メソッドのセットを定義します。

3.2 複合インターフェース

[編集]
type ReadWriter interface {
    Reader
    Writer
}

既存のインターフェースを埋め込んで新しいインターフェースを作成できます。

3.3 空インターフェース

[編集]
type Any interface{}  // Go 1.18より前の書き方

メソッドを持たないインターフェースは、任意の型の値を保持できます。Go 1.18以降はanyというエイリアスが導入されました。

4. type と func キーワードの組み合わせ

[編集]

4.1 関数型の定義

[編集]
type HandlerFunc func(w http.ResponseWriter, r *http.Request)
type Predicate func(item string) bool
type Operation func(a, b int) int

funcキーワードと組み合わせて、特定のシグネチャを持つ関数の型を定義できます。

4.2 関数型とインターフェースの組み合わせ

[編集]
type Handler interface {
    ServeHTTP(w http.ResponseWriter, r *http.Request)
}

// HandlerFuncはHandler interfaceを実装する関数型
type HandlerFunc func(w http.ResponseWriter, r *http.Request)

func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    f(w, r)
}

関数型にメソッドを追加することで、インターフェースを実装できます。これはよく使われるパターンです。

5. type キーワードとメソッド定義

[編集]

5.1 値レシーバーを持つメソッド

[編集]
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

定義した型にメソッドを追加するには、funcキーワードの後にレシーバーを指定します。

5.2 ポインタレシーバーを持つメソッド

[編集]
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

状態を変更するメソッドには、通常ポインタレシーバーを使用します。

5.3 基本型に対するメソッド定義

[編集]
type MyInt int

func (mi MyInt) IsPositive() bool {
    return mi > 0
}

基本型から派生した型にもメソッドを追加できます。これは直接基本型にはできません。

6. type キーワードとスイッチ文

[編集]

6.1 型スイッチ

[編集]
func processValue(v interface{}) {
    switch x := v.(type) {  // typeキーワードを型アサーションで使用
    case int:
        fmt.Println("Integer:", x+1)
    case string:
        fmt.Println("String:", len(x))
    case []byte:
        fmt.Println("Byte slice of length", len(x))
    default:
        fmt.Println("Unknown type")
    }
}

switch文と「.(type)」構文を組み合わせて、インターフェース値の実際の型に基づいて処理を分岐できます。

7. type キーワードとジェネリクス (Go 1.18以降)

[編集]

7.1 ジェネリック型の定義

[編集]
type List[T any] struct {
    data []T
}

func (l *List[T]) Add(item T) {
    l.data = append(l.data, item)
}

typeキーワードと型パラメータを組み合わせて、ジェネリック型を定義できます。

7.2 型制約の使用

[編集]
type Number interface {
    int | int32 | int64 | float32 | float64
}

type Vector[T Number] []T

func Sum[T Number](values []T) T {
    var sum T
    for _, v := range values {
        sum += v
    }
    return sum
}

型パラメータに制約を追加して、特定の型のみを受け入れるようにできます。

7.3 型パラメータと構造体

[編集]
type Pair[K, V any] struct {
    Key   K
    Value V
}

type Map[K comparable, V any] struct {
    data map[K]V
}

複数の型パラメータを使用して、より柔軟なジェネリック型を定義できます。

8. type キーワードと const、var の組み合わせ

[編集]

8.1 型付き定数

[編集]
type Direction int

const (
    North Direction = iota
    East
    South
    West
)

constキーワードと組み合わせて、特定の型の定数を定義できます。

8.2 型付き変数

[編集]
type Config struct {
    Timeout int
    Retries int
}

var defaultConfig = Config{
    Timeout: 30,
    Retries: 3,
}

varキーワードと組み合わせて、特定の型の変数を宣言できます。

9. type キーワードと演算子

[編集]

9.1 型アサーション

[編集]
func getValue(v interface{}) string {
    if str, ok := v.(string); ok {
        return str
    }
    return ""
}

「.(型名)」構文を使用して、インターフェース値を特定の型に変換できます。

9.2 型変換

[編集]
var i int = 42
var f float64 = float64(i)  // 明示的な型変換
var u UserID = UserID(i)    // カスタム型への変換

型名を関数のように使用して、ある型から別の型に値を変換できます。

10. パッケージレベルでの型の使用

[編集]

10.1 エクスポートされた型

[編集]
// mypackage/types.go
package mypackage

// User はエクスポートされた型(大文字で始まる)
type User struct {
    Name string
    Age  int
}

大文字で始まる型名は、パッケージ外からアクセス可能です。

10.2 非エクスポート型(内部型)

[編集]
// 内部実装用の非エクスポート型(小文字で始まる)
type connection struct {
    addr   string
    secure bool
}

小文字で始まる型名は、パッケージ内部でのみ使用されます。

11. type キーワードと go, chan, map, func の組み合わせ

[編集]

11.1 チャネル型の定義

[編集]
type TaskChan chan Task
type ResultChan chan Result

func processTask(tasks TaskChan, results ResultChan) {
    go func() {
        for task := range tasks {
            // タスク処理
            results <- Result{TaskID: task.ID, Status: "完了"}
        }
    }()
}

chanキーワードと組み合わせて、特定の型のチャネルを定義できます。

11.2 マップ型の定義

[編集]
type UserMap map[string]User
type CounterMap map[string]int

func (m CounterMap) Increment(key string) {
    m[key]++
}

mapキーワードと組み合わせて、特定のマップ型を定義し、必要に応じてメソッドを追加できます。

11.3 関数型とゴルーチン

[編集]
type WorkerFunc func(int) Result

func RunWorkers(count int, fn WorkerFunc) []Result {
    results := make([]Result, count)
    var wg sync.WaitGroup
    
    for i := 0; i < count; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            results[id] = fn(id)
        }(i)
    }
    
    wg.Wait()
    return results
}

関数型を定義して、ゴルーチンと組み合わせて使用できます。

12. 実際のユースケースと実践パターン

[編集]

12.1 オプションパターン

[編集]
type Config struct {
    Timeout  time.Duration
    Retries  int
    LogLevel string
}

type Option func(*Config)

func WithTimeout(d time.Duration) Option {
    return func(c *Config) {
        c.Timeout = d
    }
}

func WithRetries(n int) Option {
    return func(c *Config) {
        c.Retries = n
    }
}

func NewClient(options ...Option) *Client {
    cfg := &Config{
        Timeout:  time.Second * 30,
        Retries:  3,
        LogLevel: "info",
    }
    
    for _, opt := range options {
        opt(cfg)
    }
    
    return &Client{config: cfg}
}

// 使用例
client := NewClient(
    WithTimeout(time.Second * 10),
    WithRetries(5),
)

関数型を使用して、柔軟な設定オプションを提供するパターンです。

12.2 ビルダーパターン

[編集]
type QueryBuilder struct {
    table   string
    columns []string
    where   []string
    params  []interface{}
}

func (qb *QueryBuilder) Select(columns ...string) *QueryBuilder {
    qb.columns = append(qb.columns, columns...)
    return qb
}

func (qb *QueryBuilder) From(table string) *QueryBuilder {
    qb.table = table
    return qb
}

func (qb *QueryBuilder) Where(condition string, params ...interface{}) *QueryBuilder {
    qb.where = append(qb.where, condition)
    qb.params = append(qb.params, params...)
    return qb
}

func (qb *QueryBuilder) Build() (string, []interface{}) {
    // SQLクエリの構築
    // ...
    return query, qb.params
}

メソッドチェーンを使用して、オブジェクトを段階的に構築するパターンです。

12.3 ミドルウェアパターン

[編集]
type Middleware func(http.Handler) http.Handler

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

func SecurityMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // セキュリティヘッダーの追加
        w.Header().Set("X-Frame-Options", "DENY")
        w.Header().Set("X-XSS-Protection", "1; mode=block")
        next.ServeHTTP(w, r)
    })
}

// 使用例
handler := LoggingMiddleware(SecurityMiddleware(http.HandlerFunc(finalHandler)))

関数型を使用して、処理のパイプラインを構築するパターンです。

12.4 型安全な列挙型

[編集]
type ResourceType int

const (
    ResourceTypeUnknown ResourceType = iota
    ResourceTypeFile
    ResourceTypeFolder
    ResourceTypeDocument
)

func (rt ResourceType) String() string {
    switch rt {
    case ResourceTypeFile:
        return "file"
    case ResourceTypeFolder:
        return "folder"
    case ResourceTypeDocument:
        return "document"
    default:
        return "unknown"
    }
}

// 使用例
resource := ResourceTypeFile
fmt.Println(resource.String()) // "file"

基本型から派生した型と定数を組み合わせて、型安全な列挙型を作成するパターンです。

12.5 戦略パターン

[編集]
type SortStrategy interface {
    Sort([]int)
}

type QuickSort struct{}

func (qs QuickSort) Sort(data []int) {
    // クイックソートの実装
}

type MergeSort struct{}

func (ms MergeSort) Sort(data []int) {
    // マージソートの実装
}

type Sorter struct {
    strategy SortStrategy
}

func (s *Sorter) SetStrategy(strategy SortStrategy) {
    s.strategy = strategy
}

func (s *Sorter) Sort(data []int) {
    s.strategy.Sort(data)
}

// 使用例
sorter := &Sorter{}
if len(data) > 10000 {
    sorter.SetStrategy(MergeSort{})
} else {
    sorter.SetStrategy(QuickSort{})
}
sorter.Sort(data)

インターフェースを使用して、アルゴリズムを交換可能にするパターンです。

まとめ

[編集]

Goのtypeキーワードは言語の型システムの中心であり、さまざまな状況で使用されます:

  1. 基本的な型定義と型エイリアス
  2. 構造体とインターフェースの定義
  3. 関数型の定義
  4. メソッドの追加
  5. 型スイッチでの使用
  6. ジェネリックプログラミング
  7. 定数と変数の型指定
  8. 型アサーションと型変換
  9. パッケージレベルでのエクスポート制御
  10. 他のキーワード(chan, map, func, go)との組み合わせ
  11. 様々なデザインパターンの実装

これらの使い方を理解し活用することで、型安全で保守性の高いGoプログラムを開発できます。typeキーワードはGo言語の表現力の源となっており、Goのシンプルでありながら強力な型システムを支える基盤です。