Go言語
気になることのメモ。
Goにおける列挙型(enum)の表現
Go言語には、多くの他のプログラミング言語に存在する列挙型(enum)はありません。しかし、Goでenumのような機能を実現する方法があります。
基本的な列挙型の表現
以下のコードは、色を表現するColor
型を定義する例です。
// 後で触れますが、これは推奨されない方法です。
type Color int
const (
Red Color = iota
Blue
Yellow
)
この例では、Color
という新しい型を定義し、int
型をベースとしています。これは、iota
が整数を生成するためです。この定義により、Red
は0、Blue
は1、Yellow
は2という値を持つことになります。
多くの場合、このような数値から具体的な文字列を取得したいという要求があります。そのため、以下のようにString
メソッドを定義します。
func (c Color) String() string {
switch c {
case Red:
return "Red"
case Blue:
return "Blue"
case Yellow:
return "Yellow"
default:
return "Unknown"
}
}
注意点と改善方法
上記の方法は、一見すると問題なく動作するように見えますが、実は問題点があります。
Goにおけるint
型のゼロ値は0です。このため、Color
型のゼロ値も0となります。上記の例では、ゼロ値がRed
に対応してしまうため、何も指定しない場合のデフォルトがRed
となってしまいます。
この問題を解決するためには、ゼロ値をUnknown
として定義するか、iota+1
で数え始める方法があります。
type Color int
const (
UnknownColor = iota // ゼロ値をUnknownとして定義
Red
Blue
Yellow
)
const (
Red Color = iota + 1 // 1から数え始める
Blue
Yellow
)
- 個人的には、
uint
で定義するのが好きです。 uint型を使用することで、負の値を持たない列挙型を作成できる。
追加情報
- Stringerインターフェース:
String
メソッドは、Goの組み込みインターフェースであるStringer
インターフェースを実装しています。これにより、fmt.Println
などの関数でColor
型の変数を直接出力すると、定義した文字列が表示されます。 - 列挙型の拡張: 列挙型を拡張する場合、新しい値を追加する際にはリストの最後に追加することで、既存のコードの動作を変更しないようにすることが推奨されます。
- 列挙型の比較:
列挙型の値は、基本型(この場合は
int
)として比較することができます。これにより、switch
文やif
文での条件分岐が容易になります。 - 列挙型の安全性: Goでは、列挙型のようなものを使うことで、特定の値のセットのみを受け入れるように型を制限することができます。これにより、不正な値が設定されるのを防ぐことができます。。
参考文献
Goのstructとメソッド
GoはJavaやC++のような伝統的なオブジェクト指向言語とは異なり、class
の概念がない。その代わり、struct
を使ってデータ構造を定義し、それに関連するメソッドを結びつけることができる。
継承の欠如: Goには継承の概念がない。しかし、
struct
の中に別のstruct
を組み込むことで、継承のような機能を模倣することができる。これにより、多重継承の問題を回避することができる。インターフェースの暗黙性: Goのインターフェースは他の言語とは異なり、型がインターフェースのメソッドを持っていれば、明示的に宣言することなくそのインターフェースを実装するとみなされる。これはコードの柔軟性と再利用性を高める。
ポリモーフィズム: Goはインターフェースを利用してポリモーフィズムをサポートしている。これにより、異なる型でも同じインターフェースを実装していれば、同じように扱うことができる。
カプセル化: Goでは名前の先頭が大文字であればそれは公開され、小文字であれば非公開となる。これにより、パッケージの境界をもとにしたカプセル化が可能となる。
Goのインターフェースの詳細
基本的なインターフェースの定義:
Speaker
というインターフェースはSpeak
メソッドを持つことを要求する。
type Speaker interface {
Speak() string
}
インターフェースの実装:
Dog
とCat
はそれぞれSpeak
メソッドを実装している。Goでは、これだけでSpeaker
インターフェースを実装しているとみなされる。
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct{}
func (c Cat) Speak() string {
return "Meow!"
}
インターフェースの利用:
Introduce
関数はSpeaker
インターフェースを引数として受け取り、そのSpeak
メソッドを呼び出す。
func Introduce(s Speaker) {
fmt.Println("The animal says:", s.Speak())
}
func main() {
d := Dog{}
c := Cat{}
Introduce(d) // 出力: The animal says: Woof!
Introduce(c) // 出力: The animal says: Meow!
}
カプセル化の詳細
カプセル化は、データとそのデータを操作するメソッドを一つの単位にまとめ、外部からの不正なアクセスや変更を防ぐオブジェクト指向の核心的な概念である。Goでは、公開と非公開の2つの可視性のみをサポートしており、これによりシンプルかつ効果的なカプセル化が実現されている。
Goにおけるカプセル化の利点
Goのカプセル化のアプローチは以下の利点を持つ。
- シンプルさ: Goのカプセル化は名前の先頭文字の大文字・小文字で実現。
private
,protected
,public
のような修飾子は不要。 - 明確なパッケージの境界: Goのカプセル化はパッケージレベルで行われる。どの部分が外部に公開されているか、どの部分が非公開かが明確。
- データの整合性の維持: 非公開のフィールドやメソッドを通じてのみデータにアクセスできる。データの整合性を維持しやすい。
Goにおけるカプセル化の実践
Goでカプセル化を実践する方法は以下の通り。
ゲッターとセッター: 非公開のフィールドに対して、ゲッター(getter)やセッター(setter)を提供。フィールドの直接的なアクセスを制限しつつ、必要な操作を許可。
type Circle struct { radius float64 } func (c *Circle) Radius() float64 { return c.radius } func (c *Circle) SetRadius(r float64) { if r > 0 { c.radius = r } }
非公開のヘルパーメソッド: 複雑な操作や計算を非公開のヘルパーメソッドで定義。公開メソッドからそれを呼び出すことで、内部のロジックを隠蔽。
インターフェースを利用したカプセル化: 具体的な型の詳細を隠蔽し、インターフェースを公開。実装の詳細を隠すことができる。
Goは独自の方法でカプセル化をサポートし、データの保護と整合性を維持することができる。
「カプセル化についてまとめたけど、Goでこれをやるメリットはあまり感じられないので使える時に使おうという感想」
ゼロ幅のメモリ割り当て
ゼロ幅のメモリ割り当ての用途は主に、パフォーマンスの最適化やメモリ使用量の削減にある。
type key struct{}
var p key = struct{}{}
特別なメソッド
Go言語では、特定のインターフェイスを実装することにより、型に対して追加の振る舞いを定義することができます。その中でも特によく使われるのが、String
メソッドとGoString
メソッドです。
String
メソッド
String
メソッドは、fmt.Stringer
インターフェイスを実装することによって定義されます。このインターフェイスは、String() string
というシグネチャを持つ単一のメソッドを要求します。このメソッドを実装することにより、その型のインスタンスを文字列として表現する方法をカスタマイズできます。fmt
パッケージの関数(例えばfmt.Println
やfmt.Printf
など)を通じてオブジェクトが出力される際に、このString
メソッドが自動的に呼び出され、その返り値が使用されます。
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years)", p.Name, p.Age)
}
上記の例では、Person
型にString
メソッドを実装しています。このメソッドは、Person
インスタンスを"Name (Age years)"
の形式の文字列に変換します。
GoString
メソッド
GoString
メソッドは、fmt.GoStringer
インターフェイスを実装することで定義されます。このインターフェイスはGoString() string
というメソッドを要求し、このメソッドはオブジェクトのGo言語における文字列表現を提供することを目的としています。fmt
パッケージの%#v
のようなフォーマット指定子を用いた出力時に、このGoString
メソッドがあれば、それが呼び出されます。
func (p Person) GoString() string {
return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age)
}
上記の例では、Person
型にGoString
メソッドを実装しています。このメソッドは、Person
インスタンスをPerson{Name: "Name", Age: Age}
の形式のGoのコードとして表現する文字列に変換します。これはデバッグ時にオブジェクトの状態を明確に理解するのに役立ちます。
まとめ
String
メソッドは、オブジェクトの文字列表現をカスタマイズするために使用され、fmt.Stringer
インターフェイスを通じて定義されます。GoString
メソッドは、オブジェクトのGo言語における文字列表現を提供し、fmt.GoStringer
インターフェイスを通じて定義されます。- これらのメソッドを適切に実装することで、ログ出力やデバッグ情報の表示をより有効にすることができます。