0
0

More than 1 year has passed since last update.

【Golang】Structオリエンテッドまとめ

Last updated at Posted at 2022-05-08

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)
	}
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0