39 メソッドとポインタレシーバーと値レシーバー
メソッド
構造体の値を関数で使用する場合
package main
import "fmt"
type Ver struct {
X, Y int
}
func Area(v Ver) int {
return v.X * v.Y
}
func main() {
v := Ver{3, 4}
fmt.Println(Area(v))
}
12
Area()と構造体Verに結びつきがない
値レシーバー
オブジェクト指向のクラスとメソッドのように、構造体のメソッドのような形が作れる
package main
import "fmt"
type Ver struct {
X, Y int
}
func (v Ver) Area() int {
return v.X * v.Y
}
func main() {
v := Ver{3, 4}
fmt.Println(v.Area())
}
12
ポインタレシーバー
package main
import "fmt"
type Ver struct {
X, Y int
}
func (v Ver) Area() int {
return v.X * v.Y
}
func (v *Ver) Scale(i int) {
v.X = v.X * i
v.Y = v.Y * i
}
func main() {
v := Ver{3, 4}
v.Scale(10)
fmt.Println(v.Area())
}
1200
メソッドの構造体名にアスタリスクをつけることで、構造体の中身を書き換えることができる
40 コンストラクタ
外にあるpackageを呼び出すやり方
ストラクトに定義した値を大文字ではなく小文字にすると、packageの外から編集することができなくなる
package main
import "fmt"
type Ver struct {
X, Y int
}
func (v Ver) Area() int {
return v.X * v.Y
}
func (v *Ver) Scale(i int) {
v.X = v.X * i
v.Y = v.Y * i
}
func New(x, y int) *Ver {
return &Ver{x, y}
}
func main() {
v := New(3, 4)
v.Scale(10)
fmt.Println(v.Area())
}
New()を使用することで、他のpackageから生成したpackageを呼び出すときに使用するv
41 Embedded
クラスの継承のようなもの
package main
import "fmt"
type Ver struct {
X, Y int
}
func (v Ver) Area() int {
return v.X * v.Y
}
func (v *Ver) Scale(i int) {
v.X = v.X * i
v.Y = v.Y * i
}
type Ver3D struct {
Ver
Z int
}
func (v Ver3D) Area3D() int {
return v.X * v.Y * v.Z
}
func (v *Ver3D) Scale(i int) {
v.X = v.X * i
v.Y = v.Y * i
v.Z = v.Z * i
}
func New(x, y, z int) *Ver3D {
return &Ver3D{Ver{x, y}, z}
}
func main() {
v := New(3, 4, 5)
v.Scale(10)
fmt.Println(v.Area3D())
}
43 インターフェースとダックタイピング
インターフェースはPHPのものとほぼ同様
package main
import "fmt"
type Human interface {
Say()
}
type Person struct {
Name string
}
func (p *Person) Say() {
p.Name = "Mr." + p.Name
fmt.Println(p.Name)
}
func main() {
var mike Human = &Person{"Mike"}
mike.Say()
}
Humanインターフェースを実装した変数mikeにアンパサンドをつけたPersonを代入する
package main
import "fmt"
type Human interface {
Say() string
}
type Person struct {
Name string
}
func (p *Person) Say() string {
p.Name = "Mr." + p.Name
return p.Name
// fmt.Println(p.Name)
}
func DriveCar(human Human) {
if human.Say() == "Mr.Mike" {
fmt.Println("Run")
} else {
fmt.Println("Get out")
}
}
func main() {
var mike Human = &Person{"Mike"}
mike.Say()
}
インターフェースを実装することにより、呼び出される側に呼び出す側によって条件を分岐する処理を追加しない。
これをダックタイピングという(オブジェクト指向の本の復習)
44 タイプアサーションとswitch type
package main
import "fmt"
func do(i interface{}) {
ii := i.(int)
i = ii * 2
fmt.Println(i)
}
func main() {
do(10)
}
関数の引数にinterfaceを指定すると、どんな型でも受け取る事ができる。
ただ、
ii := i.(int)
のように型を設定してやらないとエラーとなる
これをタイプアサーションという
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println(v * 2)
case string:
fmt.Println(v + "!")
}
}
func main() {
do(10)
do("aaaa")
}
型によって分岐をする場合、switch文を使用する。
45 Stringer
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("My name is %v.", p.Name)
}
func main() {
mike := Person{"Mike", 22}
fmt.Println(mike)
}
My name is Mike.
ストラクトにString()を実装すると、Printlnで出力する際のフォーマットを変更することができる
46 カスタムエラー
package main
import "fmt"
type UserNotFound struct {
Username string
}
func (e *UserNotFound) Error() string {
return fmt.Sprintf("User not found: %v", e.Username)
}
func myFunc() error {
ok := false
if ok {
return nil
}
return &UserNotFound{Username: "mike"}
}
func main() {
if err := myFunc(); err != nil {
fmt.Println(err)
}
}