[Go言語]データ型

# 概要
* [前回](https://qiita.com/hasesiu/items/cbdb7e9c9a4d13886485)の続き。
* 今回はデータ型について学習した
    * 基本データ型 - int, bool, float, rune, string
    * 合成型 - 配列, リスト, Map, 構造体
* javaやts(typescript)との違いも言及しつつ進める

インプットの教材

https://www.oreilly.co.jp/books/9784814400041/

# 基本データ型

* 整数や浮動小数等は、型の種類が複数あるが、とりあえずデフォルト型のintとfloatを押さえればよいか
* 他の基本データ型はjavaと大体一緒。`rune`が聞きなじみがないが、文字を格納する`char`のことだった。

## 変数の宣言
 * `var 変数名 データ型 = 値` の形式。データ型は省略すると値に応じたデフォルトの型となる
* その他、一括宣言`var{}`や、関数内宣言 `:= 値`がある。関数内宣言はjavaやtsでは見ないのでチェック。

```go:変数宣言とデータ型の例

	// 整数
	var i1 int = 10
	var i2 = 10
	// 浮動小数
	var f1 float64 = 10.5
	var f2 = 10.5
	// bool
	var b1 bool = true
	var b2 = false
	// string
	var s1 = "hello"
	var s2 string = "hello2"
	// rune
	var r1 = 'h'
	var r2 rune = 'h'

	// 一括宣言
	var i3, s3 = 20, "abc"
	var (
		i4 = 10
		i5 int
		s4,s5 = "aa","bb"
	)

	// 関数内での変数宣言
	i6 := 10
```

* 暗黙的な型変換は行われない。明示的な型変換を毎回行う。
  * Javaでは、小さい型から大きい型への代入は暗黙的な型変換が行われる。（逆の大きい型から小さい方への変換は明示的な型変換（キャスト）が必要。）
  * Goの場合、型変換を必須にすることでルールをシンプルにした模様。トレードオフで記載が冗長になるが。

```go:型変換
    
	// 型のキャスト
    var i1,f1 = 10, 10.5 // int, float
    //f1 = i1　        // NG:暗黙的キャストはできない
	f1 = float64(i1) // OK:int → floatへの変換は明示的に行う。
	b1 = s1 == s2 // bool型は真偽値の結果で入れる。

    // なお、int型からstring（またはその逆）への変換はGoでは互換性がない
	castNum := 10
	//castStr := string(castNum)  //NG


```

## 定数の宣言

* constで行う。`const 定数名 = 値`
* 定数名は変数名と同じcamelCase。
    * 大文字から始まるプロパティは、パッケージ外からアクセスできるものを定義するルールらしい。
    * 変数や定数について、宣言方法、命名規則などtsに近い。
* 定数は、イミュータブルな値のみ設定できる。
    * 基本データ型（リテラル値）は設定できる。イミュータブルな値であるため。
    * 配列や構造体などはイミュータブルな値であるため設定できない。

```go:定数の宣言
	const con1 = "定数"
	const (
		con2 = "const"
		con3 = "const2"
	)

```

# 配列とスライス

* 配列は固定長のデータをリスト管理
* スライスは可変長のデータリストを管理。基本的にはスライスを使う。(Javaのlist）
* スライス（配列も）はミュータブルなデータ型であることに注意
* 値の追加（append）では新しいスライスを作成するため、戻り値への代入が必要

```go:配列、sliceの作成
	// 配列の作成
	var l1 = [3]int{10,20,30}
	var l2 = [...]string{"aa","bb"}
	// スライス
	var s1 = []int{10,20,30}
	// スライスは追加できる（戻り値が必要）
	s1 = append(s1, 40);
```

* コピーする場合は、makeで同じ領域の空スライスを作って、copy処理でコピーする方法がよい。

```go:sliceのコピー

	s := []int{10,20,30,40}
	// makeでコピー元と同じ長さの配列を用意
	s2 := make([]int, len(s))
	// copyを実施
	copy(s2,s)
	s2 = append(s2, 50)
    fmt.Println(s, s2) //[10 20 30 40] [10 20 30 40 50]

```

# マップ

* `map[キーの型]値の型`で宣言。初期値は`nil`
* mapへの追加は、`m[key] = 値`。keyはなければ追加
* mapの参照は`m[key]` 。値がなければデフォルト値を返す。
* mapの削除は、`delete(m, key)`。戻り値なし。

```go
	// 宣言
	var m1 map[int]string  // NG: nilマップ。値の追加不可となるためこの宣言はしない。
	m2 := map[int]string{} // OK: 空マップの追加。値の追加可能

	// 値の読み書き
	m2[1] = "値1"
	m2[2] = "値2"
	m2[3] = "値3"
	fmt.Println(m2[2]) // 値2

	// 削除
	delete(m2, 2)
	fmt.Println(m2[2])   // 値なし：nil
	fmt.Println(len(m2)) // 2
```

* カンマokイディオム
    * 値と、値の存在可否を取得するイディオム。
        * キーに紐づく値が存在しない場合、値の型のデフォルト値がとれる
        * そのため、値が存在しないのか、デフォルト値を設定しているかの区別をつけるため、存在可否も返す。
    * `v, ok :=m[key]` の構文で判別ができるようになる。
```go
	m3 := map[string]int{
		"first": 0,
		"second": 1,
	}
	val, ok := m3["first"]
	fmt.Println(val,ok) // 0, true → キーが存在し、値が0
	val2, ok2 := m3["notkey"]
	fmt.Println(val2,ok2) // 0, false　→　キーがなく、値はデフォルト0

```

# 構造体

* 構造体の継承はできない。（Javaのクラスとは異なる。）
* `type 変数名 struct{}`キーワードで作成する(構文はtsのtypeと近しい。structがあるかないか。）
    * `type`は型。`struct`が構造体を表す。

```go
	// 構造体の作成
	type human struct {
		name string
		age int
	}
	
	// 初期化
	var taro human
	hanako := human{} //Mapとは違い、varと同様に、0値で初期化。
	takashi := human{"たかし",20}
	yuki := human{name: "ゆき"}

	fmt.Println(taro) // { 0}
	fmt.Println(hanako) // { 0} 
	fmt.Println(takashi) // {たかし 20}
	fmt.Println(yuki) // {ゆき 0}

	// 値の設定
	hanako.name = "はなこ"
	fmt.Println(taro) // {はなこ 0}
```

* 無名構造体は変数宣言と同時に作成する。型は定義しないため、`struct`キーワードのみ

```go
	// 無名構造体
	var game struct {
		title string
		price int
	}
	game.title = "FF"
	game.price = 7000

	// 無名構造体 - 初期化時に設定（リテラル代入）
	game2 := struct {
		title string
		memo string
	} {
		title: "DQ",
		memo: "楽しい",
	}
```

* 別の型への代入は、構造体の構成が一致する場合可能。

```go
	type human1 struct {
		name string
		age int
	}
	h1 := human1{}

	type human2 struct {
		name string
		age int
	}
	h2 := human2{}

	type human3 struct {
		age int
		name string
	}
	h3 := human3{}

	// 構造体の構成が一致する場合、型変換で代入できる
	h1 = human1(h2) // OK: 一致する
	h1 = human1(h3) // NG: 一致しない。（コンパイルエラー）
	
	// 無名関数の場合は直接代入できる
	var h4 struct {
		name string
		age int
	}
	h1 = h4
```

# おわりに

* 基本型、配列、スライス、マップ、構造体について学んだ。細かい違いはあれど、概ねJavaやtsと同じ。
    * 宣言方法や、構造体の書き方など、tsの方が構文は近いかも。
    * sliceやMapの種類は一つ、Setはないなど、Goの方がシンプルではある。
        * この辺りは、継承がなく、派生クラスがないからか。

* ただし、継承がないのは大きな違いに感じる。
    * オブジェクト指向による設計は難しくなりそうなので、大規模開発には向いてなさそう。
    * マイクロサービス化が主流になってきているので、シンプルな設計にしているのかも。
* 関数内宣言（名称これでいいのか？）`:=`による変数宣言は、Javaやtsにはない構文なので覚えておく。