方法
メソッドは、特別なレシーバー引数を持つ関数です。
func (v Vertex) Abs() float64{
return math.Sqrt(v.X*v.X + v.Y*v.Y)
// In this example, the `Abs` method has a receiver of type `Vertex` named `v`
}
// is equivalent to (in terms of functionality)
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// You can declare a method on non-struct types, too.
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
// You can only declare a method with a receiver whose type is defined in the same package as the method. You cannot declare a method with a receiver whose type is defined in another package (which includes the built-in types such as int).
ポインターレシーバー
メソッドのレシーバーはポインターにすることができます。これは、レシーバータイプが*T
いくつかのタイプのリテラル構文を持っていることを意味しますT
。(また、T
それ自体をなどのポインタにすることはできません*int
。)
type Vertex struct {
X, Y float64
}
// golang pointer receiver method
func (v* Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f // recall that v.X is the same as dereferencing (*v).X
}
// it can be written as, again, a function
func ScaleFunc(v* Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
メソッドはレシーバーを変更する必要があることが多いため、ポインターレシーバーはバリューレシーバーよりも一般的です。
ポインターレシーバーを使用するメソッドとポインター引数を使用する関数
ポインタレシーバを使用するメソッドとポインタ引数を使用する関数には1つの違いがあります。
// func with ptr arg
var v Vertex
ScaleFunc(v, 5) // compile error
ScaleFunc(&v, 5) // works
// method with ptr receiver
var v Vertex
p := &v
p.Scale(5) // works
v.Scale(5) // also works; auto-referencing
つまり、便宜上、Goはステートメントv.Scale(5)
を(&v).Scale(5)
、Scale
メソッドにポインターレシーバーがあるためと解釈します。これは通常の機能では機能しません。
同等のことが逆方向に起こります。値レシーバーを持つメソッドと値引数を持つ関数がある場合も同じことが起こります。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func AbsFunc(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// func with val arg
var v Vertex
fmt.Println(AbsFunc(v)) // works
fmt.Println(AbsFunc(&v)) // compile error
// method with val receiver
var v Vertex
fmt.Println(v.Abs()) // works
p := &v
fmt.Println(p.Abs()) // also works; (*p).Abs(); auto-dereferencing
この場合、メソッド呼び出しp.Abs()
はとして解釈され(*p).Abs()
ます。
値またはポインターレシーバーの選択
ポインタレシーバを使用する2つの理由:
- ポインターレシーバーを使用するメソッドは、レシーバーが指す値を変更できます。
- 各メソッド呼び出しで値をコピーすることは避けてください。レシーバーが大きな構造体の場合、これはより効率的です。
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
この例では、Abs
メソッドはレシーバーを変更する必要はありませんが、レシーバータイプを使用してい*Vertex
ます。
一般に、特定のタイプのすべてのメソッドには、値またはポインターレシーバーのいずれかが必要ですが、両方が混在していてはなりません。
インターフェイス
基本的なインターフェース
インターフェイスタイプは、メソッドシグネチャの集合として定義されます。
インターフェイスタイプの値は、これらのメソッドを実装する任意の値を保持できます。
type Abser interface {
Abs() float64
}
func main() {
var a1 Abser // a value of Abs() interface type
var a2 Abser // another value of Abs() interface type
f := MyFloat(-1.414) // default constructor
v := Vertex{
3, 4}
a1 = f // a MyFloat implements Abser
a2 = &v // a *Vertex implements Abser
a2 = v // invalid! Vertex (the value type) doesn't implement Abser because the Abs method is defined only on *Vertex (the pointer type).
fmt.Println(a1.Abs())
fmt.Println(a2.Abs())
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
インターフェイスは暗黙的に実装されます
型は、そのメソッドを実装することによってインターフェースを実装します。意図の明示的な宣言はなく、「実装」キーワードもありません。
暗黙的なインターフェースは、インターフェースの定義をその実装から切り離します。これは、事前の準備なしで任意のパッケージに表示される可能性があります。
インターフェイス値
内部的には、インターフェース値は、値と具象型のタプルと考えることができます
(value, type)
。インターフェース値は、特定の基礎となる具象型の値を保持します。
インターフェイス値でメソッドを呼び出すと、基になる型で同じ名前のメソッドが実行されます。
基になる値がnilのインターフェース値
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M() // If the concrete value inside the interface itself is nil, the method will be called with a nil receiver.
i = &T{
"hello"}
describe(i) // Note that an interface value that holds a nil concrete value is itself non-nil.
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
一部の言語では、これによりnullポインター例外がトリガーされますが、Goでは、nilレシーバーでの呼び出しを適切に処理するメソッドを作成するのが一般的です(M
この例のメソッドのように)。
nil
インタフェース値はどちらも値も具体的な形を保持しています。
nilインターフェイスでメソッドを呼び出すと、実行時エラーになります。これは、インターフェイスタプル内に、呼び出す具体的なメソッドを示すタイプがないためです。
type I interface {
M()
}
func main() {
var i I
describe(i)
i.M() // panic: runtime error: invalid memory address or nil pointer dereference, i is nil itself!
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
空のインターフェース
ゼロメソッドを指定するインターフェイスタイプは、空のインターフェイスと呼ばれます。
interface{}
空のインターフェイスは、任意のタイプの値を保持できます。(すべてのタイプは少なくともゼロのメソッドを実装します)。
func main() {
var i interface{
}
describe(i)
i = 42
describe(i)
i = "hello"
describe(i)
}
func describe(i interface{
}) {
fmt.Printf("(%v, %T)\n", i, i)
}
空のインターフェイスは、不明なタイプの値を処理するコードによって使用されます。たとえばfmt.Print
、タイプが任意の数の引数を取りますinterface{}
。
タイプアサーション
タイプアサーションは、インターフェイス値の基になる具象値へのアクセスを提供します。
func main() {
var i interface{
} = "hello"
s := i.(string) // type assertion; this statement asserts that the interface value i holds the concrete type string and assigns the underlying string value to the variable s.
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok) // "hello" and true
f, ok := i.(float64)
fmt.Println(f, ok) // 0 and false
f = i.(float64) // panic!
}
タイプスイッチ
func do(i interface{
}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
ストリンガー
最もユビキタスなインターフェースの1つは、パッケージStringer
によって定義されていfmt
ます。
type Stringer interface {
String() string
}
AStringer
は、それ自体を文字列として記述できるタイプです。fmt
パッケージ(および他の多くのパッケージ)は、値を出力するためにこのインターフェイスを探します。
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
type Stringer interface {
String() string
}
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {
127, 0, 0, 1},
"googleDNS": {
8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
エラー
Goプログラムは、エラー状態をerror
値で表します。
error
型は、内蔵されたと同様のインタフェースfmt.Stringer
:
type error interface {
Error() string
}
と同様fmt.Stringer
に、fmtパッケージは、error
値を出力するときにインターフェイスを探します。
多くのerror
場合、関数は値を返します。呼び出し元のコードは、エラーがに等しいかどうかをテストすることによってエラーを処理する必要がありnil
ます。
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
Anil error
は成功を示します。anon-nil error
は失敗を示します。
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v\n", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
var e ErrNegativeSqrt = ErrNegativeSqrt(x)
return -1, e
}
return math.Sqrt(x), nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
読者
package main
import (
"fmt"
"io"
"strings"
)
func NewReader(s string) *Reader // this is from "strings" package. NewReader returns a new Reader reading from s. It is similar to bytes.NewBufferString but more efficient and read-only.
func main)() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8) // init a byte slice of size 8, i.e., 8 bytes
/*
type Reader interface {
Read(p []byte) (n int, err error)
}
Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
*/
for {
n, err := r.Read(b)
fmt.Printf("n = %v, err = %v, b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
スキップされた概念:画像
今のところ画像はスキップされます。