「Go/メソッドとインターフェース」の版間の差分

削除された内容 追加された内容
Go 版176949から分割
 
Ef3 (トーク | 投稿記録)
→‎メソッド: Goはオブジェクト指向言語ではないのでしょうか?
タグ: 2017年版ソースエディター
1 行
== メソッド ==
Goには、C++やJavaのようなクラスベースオブジェクト指向言語にあるような class はありません。
JavaScriptなどに
またGoには、JavaScript, LuaやSelfのようなプロタイプベースオブジェクト指向言語にあるようなプロトタイプチェーンもありません。
オブジェクト.メソッド()
 
のように特定の「では、Goはオブジェクト指向言語ではるもに所属する関数とてメソッドといものがあります。か?
 
Goがオブジェクト指向言語かの議論は、オブジェクト指向言語の定義に還元されすので興味深いのですが、ここでは結論を出しません。
Go言語では、type演算子などを使って、このようなメソッドを作成できます。
 
さてタイトルの'''メソッド'''ですが、多くのオブジェクト指向言語で備えている関数の特別な形式で
メソッドの書式については文章だけで説明しても難しいので、まず先にコード例を提示します。(後述で書式などを説明します。)
オブジェクト.メソッド(引数)
の形式を取るものが多くみられます。
 
Go言語では、型と結びついたメソッドを作成できます。
;コード例
 
<syntaxhighlight lang="go">
;[https://paiza.io/projects/q_nEtbtbs0XU0L6Zaflz6A?language=go コード例]:<syntaxhighlight lang="go" highlight="5,7-9,13,14" line>
package main
 
import ( "fmt"
"fmt"
)
 
type kouNumber int // 型を定義
 
func (n kouNumber) tasugopow() kouNumber {
return n * n
// 5を足すだけの処理
return 5 + n
}
 
func main() {
n := Number(7)
var n kou
m := n.pow()
var m kou
fmt.Printf("%v(%T)\n", m, m)
n = 7
m = n.tasugo()
fmt.Printf("%d\n", m)
 
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
 
49(main.Number)
;実行結果
</syntaxhighlight >
12
;解説:<syntaxhighlight lang="go" start=5 line>
::(※ 5+7=12なので)
type Number int
 
</syntaxhighlight>
 
:組込み型にはメソッドは定義できず、型定義が必要です(この場合は Number)。
上記コードの書式を解説すると、下記のようになります。
;メソッド定義:<syntaxhighlight lang="go" start=7 line>
 
func (n Number) pow() Number {
;上記コード例の書式など
return n * n
<syntaxhighlight lang="go">
package main
 
import (
"fmt"
)
 
type 型名 型
 
func (レシーバ変数 型名) メソッド名() 型名 {
return 戻り値
}
 
func main() {
var n kou
var m kou
// 初期値など
n = 7
m = レシーバ変数.メソッド()
 
fmt.Printf("%d\n", m)
}
</syntaxhighlight>
:<code>(n Number)</code> がレシーバーで、<code>pow</code> がメソッド名です。
 
:レシーバーがあることが関数との違いで、それは呼び出し方法にもあります。
読者には
;メソッドの呼び出し:<syntaxhighlight lang="go" start=13 line>
type 型名 型
m := n.pow()
というのが冗長に思えるかもしれませんが、しかしGo言語の仕様により、(関数ではなく)メソッドを使う際には、こう書かないと、エラーになります。(たぶん、コンパイラ作成時のいろいろな事情がありますので、気にせず、「こう書くのだ」と覚えてください。)一方、メソッドではなく関数を使うだけなら、このtypeの文は不要です。
</syntaxhighlight>
 
: [[Go/定数と変数#短い変数宣言|短い変数宣言]]の構文です。
 
: <code>n.pow()</code> がメソッドコールで、関数コールにはない <code>n.</code> がメソッド独特なレシーバーです。
さて、最後のほうで「fmt.Printf("%d\n", m)」とありましが、もし、ここを
: <syntaxhighlight lang="go" start=7 line inline> func (n Number) pow() Number { return n * n } </syntaxhighlight> の戻り値の型も Number なので、m の型は Number と型推論されます。
fmt.Printf("%d\n", n.nibai() )
;fmt.Printfで表示:<syntaxhighlight lang="go" start=14 line>
のように一度にまとめて書いても、エラーになります。
fmt.Printf("%v(%T)\n", m, m)
 
</syntaxhighlight>
なので、
:C言語のprintf()にはない型指定子 <code>%v</code>(値を表示)と <code>%T</code>(型名を表示)を使っています。
m = n.nibai()
::「なぜユーザー定義型の型名を知っているのか?」
fmt.Printf("%d\n", m)
:というのが最もな疑問だと思います。
のように、組み込み関数の外で、メソッドの値を、べつの変数に代入する必要があります。(よそのプログラム言語でも、似たような現象があり、こういう作業を「インスタンス」という。メモリの確保などのコンパイラ内部などの都合により、あらかじめ別の変数に代入するなどの必要がある。)
: ライブラリーのソースコード go/src/fmt/print.go を読むのが最適なのですが、結論から言うと '''reflect''' パッケージの <code>TypeOf</code> 関数を呼び出しています。
 
: <code>reflect.TypeOf</code> は go/src/reflect/type.go で定義されています。
 
: この様に、GoのパッケージはGo自身で書かれていますし、go(コンパイラーやパッケージマネージャーのフロントエンド)もGoで書かれているので、コーディング例の宝庫と言えます。
;レシーバ変数
func (n kou) tasugo() kou {
において、funcの直後の ( ) のなかの変数をレシーバ変数と言います。
 
つまり、書式は
func (レシーバ変数 型名) メソッド名() 型名 {
です。
 
本節の冒頭のコード例
func (n kou) tasugo() kou {
では、「n」がレシーバ変数です。
 
 
メソッドを呼び出すときの書式は、
レシーバ変数.メソッド()
の書式です。
 
そして、これを「インスタンス」(メモリ確保などのための事前処理)のために、別の変数(たとえばmなど)に代入する必要があるわけですから、
m = レシーバ変数.メソッド()
みたいな記述がコード中のどこかにあるコーディングをする事になるでしょう。
 
== インターフェース ==