【GO】Golangの勉強を始めたから殴り書きのメモ~その3
つづき。 tour.golang.org
Methods
- メソッドの定義(レーシーバー)
package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } // Vertex型のレシーバーをもち、float64型の返り値をもつ、Absという名前の関数 func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } type MyFloat float64 // typeでも定義できる func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) } func main() { v := Vertex{3, 4} // 呼び出す fmt.Println(v.Abs()) // 呼び出す f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs()) }
- メソッドの定義(通常)
package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } // Vertex型を引数にもち、float64型を返り値にもつ、Absという名前の関数 func Abs(v Vertex) float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 9} // 呼び出す fmt.Println(Abs(v)) }
- ポインタレシーバ
package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } // ポインタレシーバ func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } // 変数レシーバ func (v Vertex) Scale2(f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} // ポインタレシーバーを使う v.Scale(10) // vの値が変わってる fmt.Println(v.Abs()) // 50 fmt.Println(v) // {30 40} g := Vertex{3, 4} // 変数レシーバを使う g.Scale2(10) // gの値は変わっていない fmt.Println(g.Abs()) // 5 fmt.Println(g) // {3 4} }
- ポインタとメソッド
package main import "fmt" type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func ScaleFunc(v *Vertex, f float64) { v.X = v.X * f v.Y = v.Y * f } func main() { v := Vertex{3, 4} v.Scale(2) // ポインタを引数に渡す ScaleFunc(&v, 10) p := &Vertex{4, 3} p.Scale(3) // = (*p).Scale(3) // pはポインタ ScaleFunc(p, 8) fmt.Println(v, p) }
- パート2
import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func AbsFunc(v Vertex) float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := Vertex{3, 4} fmt.Println(v.Abs()) // 5 // 引数はポインタではない fmt.Println(AbsFunc(v)) // 5 p := &Vertex{4, 3} fmt.Println(p.Abs()) // = fmt.Println((*p).Abs()) // 5 // 引数はポインタ fmt.Println(AbsFunc(*p)) // 5 }
-
package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs()) // Before scaling: &{X:3 Y:4}, Abs: 5 v.Scale(5) fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs()) // After scaling: &{X:15 Y:20}, Abs: 25 }
Interfaces
package main import "fmt" // インターフェースの定義 type I interface { M() } type T struct { S string } // このメソッドを定義することで、 // 明示的に書かなくても、TはI(インターフェース)を実装していることになる。 func (t T) M() { fmt.Println(t.S) } func main() { var i I = T{"hello"} // 呼び出し i.M() // hello }
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) // (&{Hello}, *main.T) i.M() // Hello i = F(math.Pi) describe(i) // (3.141592653589793, main.F) i.M() // 3.141592653589793 } func describe(i I) { fmt.Printf("(%v, %T)\n", i, i) }
package main import "fmt" // インターフェース type I interface { M() } type T struct { S string } func (t *T) M() { // レジーバーがnilでも例外を引き起こさないようにメソッドを記述するのが一般的 if t == nil { fmt.Println("<nil>") return } fmt.Println(t.S) } func main() { var i I var t *T i = t // ゼロ値はnil describe(i) // (<nil>, *main.T) i.M() // <nil> i = &T{"hello"} describe(i) // (&{hello}, *main.T) i.M() // hello } func describe(i I) { fmt.Printf("(%v, %T)\n", i, i) }
- Nil interface values
package main import "fmt" type I interface { M() } func main() { // 初期化はしない var i I // 実行される describe(i) // (<nil>, <nil>) // ランタイムエラー i.M() } func describe(i I) { fmt.Printf("(%v, %T)\n", i, i) }
- The empty interface
package main import "fmt" func main() { // 空のインターフェース var i interface{} describe(i) // (<nil>, <nil>) i = 42 describe(i) // (42, int) i = "hello" describe(i) // (hello, string) } func describe(i interface{}) { fmt.Printf("(%v, %T)\n", i, i) }
- 型の検証
package main import "fmt" func main() { // インターフェースに元となる値を代入 var i interface{} = "hello" // iがstring型を保持しているか検証 s := i.(string) // stringでない場合は、panic! fmt.Println(s) // hello // iがstring型を保持しているか検証(bool値付き) s, ok := i.(string) // stringを保持すればTrue,保持しない場合はFalseでpanicは起きない。 fmt.Println(s, ok) // hello true f, ok := i.(float64) fmt.Println(f, ok) // 0 false f = i.(float64) // panic fmt.Println(f) }
- Type switches
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) // Twice 21 is 42 do("hello") // "hello" is 5 bytes long do(true) // I don't know about type bool! }