1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Go初学者が学んだことまとめ〜その3〜(構造体、インターフェース)

Last updated at Posted at 2018-10-01

#はじめに
こちらの続きです。
Go初学者が学んだことまとめ〜その1〜(構成と実行、構文)
Go初学者が学んだことまとめ〜その2〜(参照型)

今回は、ポインタ、構造体、インタフェースについてです。

#ポインタ
基本的にはC言語のポインタと同じ(ここでは、ポインタとは何かという話はしない)。
Goでポインタを必要とする場面は限定的。
しかし、GoはC言語と同様に複雑なポインタ定義を使用できる。
なぜそのような言語仕様が存在するのか。それは、Cとの互換性のためだと考えられる。
cgoを使うとCで定義された関数をGoから呼び出すことができる。このような時にCと同様のポインタがGoで使えると、連携が楽になり、Cの既存資産を活かしやすくなる。

#構造体
複数の任意の型の値を1つにまとめたもの。
Goにクラスはないが、構造体でクラス的なものを表現できる。

##定義

//構造体の定義
type Point struct {
	X int
	Y int
}

//変数定義
var pt1 Point
pt1.X = 10
pt1.Y = 8

//リテラル
pt2 := Point{4, 2}
pt3 := Point{X: 3, Y: 6}

##構造体を含む構造体
Goには継承の機能はないが、構造体の中に構造体を埋め込むことでいわゆる継承的なことが実現できる。

type User struct {
	Id   int
	Name string
}

type Customer struct {
	User //省略表記
	//User       User //省略しない場合はこのように書く
	CustomerId int
}

type Admin struct {
	User
	Authority string
}

customer := Customer{
	User:       User{Id: 11, Name: "Kai"},
	CustomerId: 3,
}

admin := Admin{
	User:      User{Id: 81, Name: "Riku"},
	Authority: "Developer",
}

fmt.Println(customer.User.Id) //11
//Customerの定義でUserを省略表記していることで、このようにUserを省略して使用することができる
fmt.Println(customer.Id) //11

fmt.Println(admin.Name)  //Riku

##構造体とポインタ
構造体は値型なので、関数の引数に構造体を渡した場合は、構造体のコピーが生成される。つまり、元の構造体にはなんの影響を与えることができない。
なので、関数の引数に構造体を使用する場合は、基本的にポインタを渡す。

引数が構造体

type Point struct {
	X int
	Y int
}

func swap(p Point) {
	x, y := p.Y, p.X
	p.X = x
	p.Y = y
}

p := Point{X: 1, Y: 2}
swap(p)
//値渡しなのでpは書き変わらない
fmt.Println(p.X) // 1
fmt.Println(p.Y) // 2

引数が構造体のポインタ
type Point struct {
	X int
	Y int
}

//引数をポインタにする
func swap(p *Point) {
	x, y := p.Y, p.X
	p.X = x
	p.Y = y
}

p := &Point{X: 1, Y: 2} //構造体のポインタを定義する
swap(p)
//ポインタを渡しているのでpは書き変わる
fmt.Println(p.X) // 2
fmt.Println(p.Y) // 1

##new
newを使って、指定した型のポインタを生成することができる。
下記は同義。

p := new(Point)
p.X = 1
p.Y = 2
p := &Point{X: 1, Y: 2}

##メソッド
特定の構造体に特化した関数を定義する仕組み。
他のオブジェクト指向言語で言うと、クラスのインスタンスメソッド的なもの。

type Point struct {
	X int
	Y int
}

//Point型のメソッドを定義
//(p *Point)はレシーバーという
//レシーバーはポインタにするのが原則
func (p *Point) ToString() {
	fmt.Printf("<%d,%d>\n", p.X, p.Y)
}

p := &Point{X: 4, Y: 7}
p.ToString() // <4,7>

###コンストラクタ
Goにはコンストラクタ機能はないが、慣習的に使われるパターンがある。

type User struct {
	Id   int
	Name string
}

//NewXXXと書くのがGoのイディオム
func NewUser(id int, name string) *User {
	user := new(User)
	user.Id = id
	user.Name = name
	return user
}

fmt.Println(NewUser(12, "Ken")) // &{12 Ken}

##タグ
Goにはタグという、フィールドにメタ情報を付与する機能がある。

type User struct {
	Id   int    "ID"
	Name string "名前"
}

jsonを使ったタグの有効活用例を示す。

package main

import (
	json2 "encoding/json"
	"fmt"
)

type User struct {
	Id   int    `json:"user_id"`
	Name string `json:"user_name"`
}

func main() {
	u := User{Id: 14, Name: "Takumi"}
	bJson, _ := json2.Marshal(u)
	fmt.Println(string(bJson)) // {"user_id":14,"user_name":"Takumi"}
}

#インターフェース
型の一種で、どのようなメソッドを実装するべきかを規定するための枠組み。
他のオブジェクト指向言語のインターフェースと似ている。

Go
type Stringify interface {
	ToString() string
}

type User struct {
	Id   int
	Name string
}
//UserにToStringを実装する
func (u *User) ToString() string {
	return fmt.Sprintf("Id: %d, Name: %s", u.Id, u.Name)
}

type Book struct {
	Isbn  int
	Title string
}

//BookにToStringを実装する
func (b *Book) ToString() string {
	return fmt.Sprintf("Isbn: %d, Title: %s", b.Isbn, b.Title)
}

//ToStringを実装した型であれば引数に取れる
func Output(s Stringify) {
	fmt.Println(s.ToString())
}

user := &User{Id: 10, Name: "Hiroshi"}
book := &Book{Isbn: 9783161484100, Title: "Deep Learning"}
Output(user) // Id: 10, Name: Hiroshi
Output(book) // Isbn: 9783161484100, Title: Deep Learning

このようにポリモーフィズムによる実装ができる。

ちなみに、TypeScriptでもインターフェースを使って同じような実装ができる。

TypeScript
interface Stringify {
    toString: () => string;
}

class User implements Stringify {
    constructor(
        private id: number,
        private name: string
    ){}

    public toString(): string {
        return `id: ${this.id} name: ${this.name}`;
    }
}

class Book implements Stringify{
    constructor(
        private isbn: number,
        private title: string
    ){}

    public toString(): string {
        return `isbn: ${this.isbn} title: ${this.title}`;
    }
}

function output(s: Stringify): void {
    console.log(s.toString());
}

const user = new User(10, "Hiroshi");
const book = new Book(9783161484100, "Deep Learning");
output(user); // id: 10 name: Hiroshi
output(book); // isbn: 9783161484100 title: Deep Learning

#まとめ
Goはオブジェクト指向の機能を持たない言語なのですが、構造体やインターフェースを使うとオブジェクト指向言語と同じようなことができそうです。
Goはオブジェクト指向言語なのでしょうか?
公式のQ&Aで答えていますね。
Is Go an object-oriented language?

Yes and no. 〜

ざっくり言うと、オブジェクト指向なプログラミングは可能だが、従来のオブジェクト指向言語とは少し違うよ、ということなのでしょう。

文法に関してはここまで。
その4ではGoのツール類についてまとめます。

#参考
スターティングGo言語

#関連
Go初学者が学んだことまとめ〜その1〜(構成と実行、構文)
Go初学者が学んだことまとめ〜その2〜(参照型)
Go初学者が学んだことまとめ〜その4〜(コマンド、パッケージ)

1
0
6

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?