Started Go Language (two) methods and interfaces

EDITORIAL

Go Before learning the language, I have a certain foundation of Java and C ++, this article is mainly based on A tour of Go written mainly want to record your own learning process, deepen their understanding

Started Go Language (two) methods and interfaces

method

Go language is no "class" of this concept, but we can define a variable methods, such as the definition of the method structure, to achieve the situation is similar to class. Here We Go first to the method definition:

What is the method

"Method": a class with a special receiver parameter function

For the method, "receiver parameters" is located in funckeywords and 方法名between:

// 定义一个结构体
type Vertex struct {
    X, Y float64
}

// 这里有一个接受者参数 v
func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    // 我们可以直接调用 v 的 Abs() 方法
    fmt.Println(v.Abs())
}

Of course, we can also be the recipient v as an argument directly, then that is a normal function, and they can achieve the same functionality:

func Abs(v Vertex) float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

Receiver parameters

In the above, we define a "receiver parameters" for the structure Vertexof the Abs()method, we can be any type of variable declaration method .

Here we use typethe keyword to define a variable type "MyFloat":

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

Note: only the type of recipient declaration methods defined in the same package, but not the other as defined packet types (including built-in types such as int) method recipient declaration.

Pointer recipients

For an understanding of other languages, we know that is actually a copy of the operating parameters of the function; and because there is "pointer", there are "arguments" and "parameter" of the points.

In Go also has a presence both, for certain types of T:

  • If the type parameter is the recipient T, it is a "parameter", modify the function does not modify the original elements;

  • If the type parameter is the recipient *T, it is the "argument", it points to the elements can be modified directly in the function, so the ability to synchronize modify the original elements.

See the examples below:

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4}
    // 对于方法,Go 可以自动在值和指针之间转换
    v.Scale(10)
    fmt.Println(v.Abs())
}

When we define the method of time, Go language will help us automatically between values and pointers ; similar to the above, if the method needs a value, but we passed is a pointer, Go can help us to automatically convert. But it is not automatically converted when using the function .

Therefore, we can directly use v.Scale(10)the execution result here is 50; if we Scale()receiver to process parameters v Vertext, the Scalefunction is passed the parameter, the copy just modify elements run the same value v, performed the result is 5.

Generally, all methods of a given type should have a pointer value or the recipient, but both should not be mixed . (We'll see why below.)

interface

Unlike in Java interfaces, in Go, the interface is an abstract type . It's like a kind of "agreement", all of the interface type can reference all the methods it provides.

"Interface Type": a set of methods defined by the signature collection

type Abser interface {
    Abs() float64
}

How to Use Interface

If a type implements an interface required by all methods , then this type will implement this interface.

We look at the following code example:

package main

import (
    "fmt"
    "math"
)

// 接口 Abser,包含方法 Abs()
type Abser interface {
    Abs() float64
}

// MyFloat 实现了 Abs() 方法
type MyFloat float64
// 无需使用 implements 关键字
func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

// *Vertex 实现了 Abs() 方法
type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f  // a MyFloat 实现了 Abser
    a = &v // a *Vertex 实现了 Abser

    // 这里被注释的语句会报错
    // v 是一个 Vertex(而不是 *Vertex),没有实现 Abser。
    // a := v

    fmt.Println(a.Abs())
    fmt.Println(b.Abs())
}

The example code, type MyFloatand type *Verteximplement a Abs()method, it is possible to interface type Abserassignment; and Vertexnot implemented, if it will be assigned an error.

At the same time, we can also see from the above code, the realization Abserwhen the interface method, we did not achieve the same interfaces as Java, with implementsexplicit declaration of keywords, which would also encourage programmers to have a clear definition of the interface.

Interface value

"Connectors" as an abstract type, it is also a value, like other values ​​can be passed as is, it can be used as parameters or return values.

Here we define an interface I, type *Tand Mimplements this interface, and then we will be the interface Ias a parameter function desribe()in:

package main

import (
    "fmt"
    "math"
)

type I interface {
    M()
}

// 实现 I 的类型 *T
type T struct {
    S string
}

func (t *T) M() {
    fmt.Println(t.S)
}

// 实现 I 的类型 F
type F float64

func (f F) M() {
    fmt.Println(f)
}

func main() {
    var i I

    i = &T{"Hello"}
    describe(i)
    i.M()

    i = F(math.Pi)
    describe(i)
    i.M()
}

// 打印传入接口的值和类型
func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}

After the operation, the output is as follows:

(&{Hello}, *main.T)
Hello
(3.141592653589793, main.F)
3.141592653589793

Can be seen, the incoming interface parameters are printed at the bottom of the types and values.

The underlying value is nil interface

If the interface of a specific value is nil, then call this method interface, in some languages (such as Java) will trigger a "null pointer exception", but in Go can be printed out correctly nil.

Note: Save the nilspecific value of the interface itself is not nil.

We use the precedent of the interface Iand type *T, make the test as follows:

1. "Interface specific value" is nil

func main() {
    var i I

    var t *T
    i = t
    describe(i)
    i.M()
}

Execution results are as follows:

(<nil>, *main.T)
<nil>

It can be seen in the i call the method M()when, Go can normally print out the <nil>value and does not complain.

2. "connectors" itself is nil

func main() {
    var i I

    describe(i)
    i.M()
}

Implementation of the above statement, describe()the method can print out (<nil>, <nil>), it indicates that the interface itself as a nilnatural value is also nil; but when i of the interface method calls will throw an exception (because Go do not know what should be called the specific type of the method).

Air Interface

"Air Interface": defines the interface method 0

interface{}

Air interface has what effect? It is possible to accept any type of value (because the method does not require air interface), so we can use the interface null process value of an unknown type . For example, fmt.Printan acceptable type of interface{}an arbitrary number of parameters.

The following example can effectively help us to understand:

func main() {
    var i interface{}
    describe(i)
    // 运行结果: (<nil>, <nil>)

    i = 42k
    describe(i)
    // 运行结果: (42, int)

    i = "hello"
    describe(i)
    // 运行结果: (hello, string)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

The above describe()function will be to empty interface as a parameter, it is possible to accept the value of any type (including nil) of.

Type assertion

"Type assertion": provides access to the underlying value of the specific value of the interface mode.

t := i.(T)

The statement asserts the interface type specific stored value T i, and t is the underlying type to the variable value of T.

If i did not hold the value of type T, the statement will trigger a panic panic.

For determining a value of the interface to save a particular type, the type assertion returns two values: the value of the underlying report and a Boolean value assertion success.

t, ok := i.(T)

This "double assignment" and the map is very similar:

  • If i holds a T, it twill be the underlying value, and okis true.

  • Otherwise, the okwill falseand t will be zero value of type T, the program does not panic .

Let's use an example to sum up the interface "type assertion ':

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)
    // 执行结果: hello

    s, ok := i.(string)
    fmt.Println(s, ok)
    // 执行结果: hello true

    f, ok := i.(float64)
    fmt.Println(f, ok)
    // 执行结果: 0 false

    f = i.(float64)
    fmt.Println(f)
    // 报错(panic)
}

Type selection

"Type selection": one kind of structure in order to select from several branches of the type of assertion.

Go apply in swtichthe statement, the interface can be for a given value of the stored types of values are compared:

switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

Type Select the type of assertion in a statement i.(T)the same syntax, but the specific type Tis replaced by a keyword type.

This type of statement to determine the value of the interface to select the value of i is stored in T or S.

  • In the case of S or T, respectively will be variable v T or S type has the value of i stored.

  • In the default (i.e. no match), the variables i and v values ​​and the same type of interface.

Note: "Type Select" only the interface type is suitable , we can not be used for other types of values.

Common Interface: Stringer

fmtDefined in the package Stringeris one of the most common interface, which is similar to the Java toString()method, we can achieve it defines how the output from the caller.

fmtPackage defined as follows:

type Stringer interface {
    String() string
}

Below we achieved through Stringerthe interface, the output from the structure defined Personvalues:

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("Person: %v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a)
    fmt.Println(z)
}

Output:

Person: Arthur Dent (42 years)
Person: Zaphod Beeblebrox (9001 years)

Common Interface: error

Go with errorvalues indicate an error condition. And fmt.Stringersimilar errortype is a built-in interfaces:

type error interface {
    Error() string
}

And fmt.Stringersimilar fmtpackage when printing error value will satisfy us to achieve (or default) error, so we can realize Error()the error message to be printed from the definition of the method.

So, whether an error how to get it? Go with the still familiar "double assignment."

Typically function returns a errorvalue, it may be determined whether the code calls the error is equal nilto error processing.

  • errorIs nil, indicates a successful execution, no error

  • errorIs not nil, represent fails, an error occurred

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err)
    return
}
fmt.Println("Converted integer:", i)

Let's use an example to try errorinterface, we realize a function prescribing, and let its argument is negative when an error occurs:

package main

import (
    "fmt"
    "math"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
    // 注意:这里要 float64(e),不然会产生死循环
    return fmt.Sprint(float64(e))
}

func Sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, ErrNegativeSqrt(x)
    }
    return math.Sqrt(x), nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

Results are as follows:

1.4142135623730951 <nil>
0 -2

It should explain why Error()not direct printing method in ewhich you want to print float64(e):

Because the fmtpacket will attempt to match the output error, achieved by the variable E Error()becomes a function of the interface errortype, fmt.Sprint(e)when invoked e.Error()to output an error message string, which is equivalent to the following code:

func (e MyError) Error() string {
    return fmt.Printf(e)
}
// e 是一个 error,上面的语句实际上是这样的
// 这也就产生来死循环
func (e MyError) Error() string {
 return fmt.Printf(e.Error())
}

Common Interface: Reader

ioPackages specify io.Readerthe interface that represents the reading from the end of the data stream .

Go standard library contains the interface many implementations , including files, network connections, compression and encryption, and so on.

io.ReaderInterface has one Read()method:

func (T) Read(b []byte) (n int, err error)

Read()Way to do two things:

  • Data filling the given byte slice;

  • Return -filled "bytes" and "error value."

In the face of the end of the data stream, it will return an io.EOFerror.

Sample code creates a strings.Readerand 8 bytes at a time to read speed of its output:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, Reader!")

    b := make([]byte, 8)
    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
        }
    }
}

Execution results are as follows:

// 第一次循环
n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
// 第二次循环
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
// 第三次循环,遇到 EOF 异常,表示读取完毕,结束循环
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""

We can "of Go A tour" through online exercises to practice on the use of Reader interface.

Below we achieve a Readertype that produces an ASCII character 'A' infinite stream.

package main

import "golang.org/x/tour/reader"

type MyReader struct{}

// TODO: 给 MyReader 添加一个 Read([]byte) (int, error) 方法

func (r MyReader) Read(b []byte) (int, error) {
    // 1.填充字节切片
    b[0] = 'A'
    // 2.返回填充的字符数和错误值
    return 1, nil
}
func main() {
    reader.Validate(MyReader{})
}

Common Interface: Image

imagePackage defines Imageinterfaces:

package image

type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x , y int) color.Color
}

Note: Bounds()The return value method "Rectangle" is actually a image.Rectangle, it is imagea statement package.

color.ColorAnd the color.Modeltype is an interface, but usually because the direct use predefined achieve image.RGBAand image.RGBAModelbe ignored. These types of interfaces and image/colorcustom package. Here you can learn more about imagethe packet.

Guess you like

Origin www.cnblogs.com/Bylight/p/11955917.html