たなしょのメモ

日々勉強していることをつらつらと

Goの学習メモ7

本日のGo

忙しすぎて中々Goの勉強ができない。

interface

Javaで実装した時以来久しぶりに触る概念。
理解するのはとても大変そう。

package main

import "fmt"

type I interface {
    M()
}

type T struct {
    S string
}

func (t T) M() {
    fmt.Println(t.S)
}

func main() {
    var i I = T{"hello"}
    i.M()
}

まずここではインターフェースを定義している。
また構造体のTに文字列helloを詰めいている。

var i I = T{"hello"}

M()によりインターフェースの構造体の中に入っているhelloを出力している。

i.M()

どんどんややこしくなってくる。

package main

import (
    "fmt"
    "math"
)

type I interface {
    M()
}

type T struct {
    S string
}

func (t *T) M() {
    fmt.Println(t.S)
}

type F float64

func (f F) M() {
    fmt.Println(f)
}

func main() {
    var i I

    i = &T{"Hello"}
    describe(i)
    i.M()

    i = F(math.Pi)
    describe(i)
    i.M()
}

func describe(i I) {
    fmt.Printf("(%v,%T)\n", i, i)
}

インターフェースを宣言したあとHelloが代入されている構造体をインタフェースに詰める。

i = &T{"Hello"}
describe(i)
i.M()

この関数をインターフェースに詰めている。

func (t *T) M() {
    fmt.Println(t.S)
}

インターフェースを宣言したあとmath.Piが代入されている構造体をインタフェースに詰める。

i = F(math.Pi)
describe(i)
i.M()

この変数をインターフェースに詰めている。

type F float64

nilを出力させようとするとエラーになるのね。

package main

import "fmt"

type I interface {
    M()
}

func main() {
    var i I
    describe(i)
    i.M()
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}

空のインターフェースが宣言できるらしい。
これによって色々な型を扱うことができるようになるらしい。便利。

package main

import "fmt"

func main() {
    var i interface{}
    describe(i)

    i = 42
    describe(i)

    i = "hello"
    describe(i)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

インターフェースの値を利用するやり方。
インターフェースには"hello"(string型)を代入している。

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64)
    fmt.Println(f)
}

string型が代入されているからfloat64の型のコードをの存在している下記コード、float64型の値を呼び出そうとしている下記コードはfalseとエラーとなる。

f, ok := i.(float64)
fmt.Println(f, ok)

f = i.(float64)
fmt.Println(f)

インタフェースの型によって分岐するswitch文が存在するのか。
go言語を学習して思うのはインタフェースを使う場面がめちゃくちゃ多いんだなと感じた。

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

Stringerインタフェースを使うと変数を文字列で出力してくれるらしい。

   package main

    import "fmt"

    type Person struct {
        Name string
        Age  int
    }

    func (p Person) String() string {
        return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
    }

    func main() {
        a := Person{"Donald", 42}
        z := Person{"Barak", 43}
        fmt.Println(a, z)
    }

ちょっとだけStringerの練習をしてみた。
[127 0 0 1]のように表示されるmap型をStringerで127.0.0.1と表示させるように変更してみた。

package main

import "fmt"

type IPAddr [4]byte

func (ip IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}

func main() {
    hosts := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for name, ip := range hosts {
        fmt.Printf("%v:%v\n", name, ip)
    }
}

errorインターフェース。
error時のログ取得に使えそう。

package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v,%s", e.When, e.What)
}

func run() error {
    return &MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}