構造体の定義
var curiosity struct {
lat float64
long float64
}
// ファミリーの定義
curiosity.lat = -4.44444
curiosity.long = 133.333333
//複合リテラルで定義も可能
cr := curiosity{-4.44, 133.3333}
メソッドを使用した構造体の例
package main
import (
"fmt"
)
type coordinate struct {
d, m, s float64
h rune
}
func (c coordinate) decimal() float64 {
sign := 1.0
switch c.h {
case 'S', 'W', 's', 'w':
sign = -1
}
return sign * (c.d*c.m/60 + c.s/3600)
}
func main() {
lat := coordinate{4, 35, 22.2, 'S'}
long := coordinate{137, 26, 30.12, 'E'}
fmt.Println(lat.decimal(), long.decimal())
}
コンストラクタを使用した構造体の初期化
package main
import (
"fmt"
)
type coordinate struct {
d, m, s float64
h rune
}
type location struct {
lat, long float64
}
func (c coordinate) decimal() float64 {
sign := 1.0
switch c.h {
case 'S', 'W', 's', 'w':
sign = -1
}
return sign * (c.d*c.m/60 + c.s/3600)
}
func newLocation(lat, long coordinate) location {
return location{lat.decimal(), long.decimal()}
}
func main() {
curiosity := newLocation(coordinate{4, 35, 22.2, 'S'}, coordinate{137, 26, 30.12, 'E'})
fmt.Println(curiosity)
}
- コンストラクタの関数の名前は
newType
orNewType
- 外部へエクスポートするなら
NewType
, 内部のみならnewType
- 外部へエクスポートするなら
(私見)構造体に対するメソッドの利点
- オブジェクト指向:クラス内部で定義されたメソッドがクラスの各プロパティまたはメソッドを使用する
- Golog:構造体をレシーバーで指定してその構造体全部を見られる(まとめが下手かもしれない。。。)
メソッドの転送
package main
import (
"fmt"
)
type temperature struct {
high, low celsius
}
type location struct {
lat, long float64
}
type celsius float64
type report struct {
sol int
temperature temperature
location location
}
func (t temperature) average() celsius {
return (t.high + t.low) / 2
}
func (r report) average() celsius {
// メソッドを転送してreportから直接average()を呼べるようにする
// これにより、ロジックを丸々コピーする必要がない
return r.temperature.average()
}
func main() {
bradbury := location{-4.44, 133.33}
t := temperature{high: -1.0, low: -78.0}
// 各フィールドを指定して代入
report := report{sol: 15, temperature: t, location: bradbury}
fmt.Printf("%+v\n", report.temperature.high)
fmt.Printf("%+v\n", report.temperature.low)
fmt.Printf("%+v\n", report.average())
}
構造体の埋め込み
type report struct {
sol int
temperature // report に temperature 型を埋め込む
// 埋め込み型と同じ名前のフィールドが生成される(この場合はtempareture)
location
}
temperature型のメソッドが使用可能になる
インターフェース
- それは何か=オブジェクト
-
それで何ができるか=インターフェース
- 型が何を行えるかに焦点を絞っている
interface型
例えば
var t interface {
talk() string
}
「引数を受け取らず、文字列を返すtalk
という名前のメソッド」を指定している方ならば、このインターフェイスは満たされる。
package main
import (
"fmt"
"strings"
)
type martian struct{}
func (m martian) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew", int(l))
}
func main() {
var talker interface {
talk() string
}
// martian型はtalk()メソッドを持つ
t = martian{}
fmt.Println(t.talk())
t = laser(3)
fmt.Println(t.talk())
}
慣習的にinterface
型には-erをつけるのが一般的
package main
import (
"fmt"
"strings"
)
type martian struct{}
func (m martian) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew", int(l))
}
type talker interface {
talk() string
}
func shout(t talker) {
louder := strings.ToUpper(t.talk())
fmt.Println(louder)
}
func main() {
shout(martian{})
shout(laser(3))
}
インターフェースの条件を満たしていれば、適用可能
例えばlaser
はtalk()メソッドを持っている→talkerインターフェイスの要件を満たす→shout()の引数になりうる
laser(3)だけだとわかりずらい
以下のように考える
a := laser(1)
fmt.Println(a)
shout(martian{})
shout(a)
- aは
laser
型で1とする -
shout(a)
によってa(laser型)はインターフェイスが満たされるのでshout()
できる - インターフェイスの要件を満たせば関数に渡す引数の型を柔軟に選ぶことができる→インターフェイスの要件さえ満たせば、要件を満たす任意の構造体を関数の引数にとれる
- インターフェイスは「君にはこれができますか?」という宣言
- 構造体のメンバにインターフェイスの要件を満たすメソッドを持つものが存在すれば、その構造体のインスタンスはインターフェイスを引数にする関数を使用できる