Go言語の型の種類
こんにちは。僕もGoを勉強し始めることになりました。
学んだ文法をまとめたいと思います。
変数
型を宣言して使う
//型を宣言する
var str string
str = "hello"
fmt.Println(str)//=>hello
fmt.Println(reflect.TypeOf(str))//=>string
型推論
//型は推論される
var str = "hello"
fmt.Println(str) //=>hello
fmt.Println(reflect.TypeOf(str)) //=>string
varを省略する
//変数宣言と初期化を行う場合に、var と型宣言を省略して「:=」演算子を用いることができる。
str := "hello"
fmt.Println(str)//=>hello
fmt.Println(reflect.TypeOf(str))//=>string
型の種類
主にこんな感じ。
int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex64 complex128
bool byte
string rune
型の変換
型の変換には"strconv"を使用する。
文字列から数値に変換する
import (
"fmt"
"strconv"
"reflect"
)
func main() {
var i int
i, _ = strconv.Atoi("100")
fmt.Println(i) //=>100
fmt.Println(reflect.TypeOf(i)) //=>int
}
数値から文字列に変換する
import (
"fmt"
"strconv"
"reflect"
)
func main() {
fmt.Println(strconv.Itoa(100)) //=>100
fmt.Println(reflect.TypeOf(strconv.Itoa(100))) //=>string
}
interface型
全ての型と互換性を持っているinterface{}型
var obj interface{}
obj = 123 // int
obj = "str" // string
obj = []string{"linux", "windows", "android"} // slice
obj = make(chan string) // channel
obj = func(val int) string { return fmt.Sprintf("number is %d", val) } // function
ポインタ型
func callByValue(i int) {
i = 20 // 値を上書きする
}
func callByRef(i *int) {
*i = 20 // 参照先を上書きする
}
func main() {
var i int = 10
callByValue(i) // 値を渡す
fmt.Println(i) // 10
callByRef(&i) // アドレスを渡す
fmt.Println(i) // 20
}
ポインタ型とそうではない型の違いは、構造体の部分で書きます。
型の確認
型の確認には、reflectを使う。
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println(reflect.TypeOf(1)) //=>int
fmt.Println(reflect.TypeOf("test")) //=>string
fmt.Println(reflect.TypeOf([]string{"a", "b", "c"})) //=>[]string
}
配列
型とサイズを決めて配列を宣言。
var arr [5]int
//5つのものが入る配列arrを宣言
//6以上入るとエラー。
var arr [5]int
arr[5] = 5
fmt.Println(arr) //=>invalid array index 5 (out of bounds for 5-element array)
宣言した数以上のものを入れることができない。
柔軟に対応するためには、どうするか=>sliceを使う。
slice
配列は入れることのできる数が決まっているが、sliceは決まっていない。可変長である。
sliceの定義方法
a:=[]T{}
a:=make([]T,len)
sliceの使い方
fruits := []string{"apple", "orange", "lemon"}
fmt.Println(fruits) // => [apple orange lemon]
fruits = append(fruits, "peach")
//追加の際には、appendを使う。
fmt.Println(fruits) //=>[apple orange lemon peach]
//peachが追加された。
map(連想配列)
Golangではハッシュ(連想配列)のことをmapと呼ぶ。
package main
import (
"fmt"
)
func main() {
m := map[string]int{"japanese": 68, "english": 70, "math": 90}
fmt.Println(m) //=>map[japanese:68 english:70 math:90]
fmt.Println(m["japanese"]) //=>68
//mapからキーの値を取り出す
m["science"] = 100
//要素を追加する
fmt.Println(m) //=>map[japanese:68 english:70 math:90 science:100]
}
構造体
struct定義
structを使ってそのフィールドが持つ値を決定することができる。
type Animal struct {
Name string
Age int
}
クラスのようなもの。
structを初期化する
初期化の方法にもいくつかあるので、見ていきます。
newを使う
さっき作ったAnimalを初期化します。初期化したanimalに値を入れます。
package main
import "fmt"
type Animal struct {
Name string
Age int
}
func main() {
animal := new(Animal)//:= でポインタ型
animal.Name = "Tanaka"//NameにTanakaを代入
animal.Age = 10//Ageに10を代入
fmt.Println(animal)
//=>&{Tanaka 10}
}
ポインタ型でTanakaと10というものが返ってきます。
ポインタ型で初期化する
package main
import "fmt"
type Animal struct {
Name string
Age int
}
func main() {
animal := &Animal{
Name: "Tanaka",
Age: 10,
}
fmt.Println(animal)
//=>&{Tanaka 10}
}
これでも同じ結果です。
そのまま初期化して使う
package main
import "fmt"
type Animal struct {
Name string
Age int
}
func main() {
animal := Animal{
Name: "Tanaka",
Age: 10,
}
fmt.Println(animal)
//=>{Tanaka 10}
}
今回はポインタ型ではなく、そのまま出力されました。
構造体の初期化方法で何が違うのか
これは一体何が違うのでしょう。
以下の記事に違いが書かれていました。引数として使った場合などに変化が見られるそうです。
https://qiita.com/kmtr/items/1e7caf92aa8bb587906d
試してみます。
package main
import "fmt"
type Animal struct {
Name string
Age int
}
func changeAge1(animal1 Animal, age int) {
animal1.Age = age
fmt.Printf("%p\n", &animal1)
}
func changeAge2(animal2 *Animal, age int) {
animal2.Age = age
fmt.Printf("%p\n", animal2)
}
func main() {
animal1 := Animal{
Name: "Tanaka",
Age: 10,
}
fmt.Printf("%p\n", &animal1)
changeAge1(animal1, 100)
animal2 := &Animal{
Name: "Tanaka",
Age: 10,
}
fmt.Printf("%p\n", animal2)
changeAge2(animal2, 100)
}
結果
0xc42000a060
0xc42000a080
0xc42000a0a0
0xc42000a0a0
なるほど、確かに以下のように構造体を初期化した場合は、引数として使うと、構造体の値を変更した際にポインタが変わってしまうようです。
animal1 := Animal{
Name: "Tanaka",
Age: 10,
}
構造体の初期化方法というよりポインタの扱い方な気がします。
Goのポインタが参考になります。
for文
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
if文
package main
import (
"fmt"
"math"
)
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4))
}
switch文
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
終わりに
Go言語触ってみましたが、正直まだまだ見えてきません。
いつか見えてくる日がくるのだろうか。その時を待ちたいと思う。