Go language portal --interface

 1, Go how to define the interface

  Go through an interface type declaration, the form

type geometry interface {
    area() float64
    perim() float64
}

  And declare a structure like interface is also by type declaration.

  Back type is the interface name, next to the keyword interface.

  inside the area defined by the interface () method and the interface geometry of perim.

 

  With the interface, it should be how to implement the interface it?

type rect struct {
    width, height float64
}

func (r rect) area() float64 {
    return r*width*height
}

func (r rect) perim() float64 {
    return 2*r*width + 2*r*height
}

  Above is rect implementation of the interface geometry code. Java language is different from these, there is an explicit representation of a keyword implement implement an interface.

 

  Spirit of contract and Java interface is somewhat different, Go inside the interface is more like a combination of concepts.

  Here we must mention the concept of a "duck type". Duck is an object-type dynamic programming language inference strategy, it is more concerned about how objects can be used, rather than the type of the object itself. That is, if something looks like a duck, quacks like a duck will, walk, swim, then we can infer that this little thing is a duck.

  Analogy above code, rect geometry that looks like a duck, it is possible the same as the geometry of the area () behavior, can be the same as the geometry perim (), rect meets all acts geometry defined, so we infer it is to achieve a rect geometry of the interface.

  In this way, we did not have to write code like the implement xxx. Original particle size down to a size class of the class of which the method.

 

  Incidentally, before making Java development time, because it is collaborative development, all with a unified framework, coupled with the popular idea of ​​programming to an interface that has become such a conditioned reflex: when writing a service the first reaction is to create a new interface, and then define the interface methods, then followed by the preparation of the implementation class, the vast majority of cases, this is the only use an implementation class, the future for a long time did not see the other interface implementation class. In order to achieve such an interface written interfaces, and sometimes make the code in small and medium projects appeared to be very rigid.

 

2, How to determine whether the realization of an interface

  Above us is how to define an interface and Go "to achieve" interface. The above code is only a rect structure, if there is more of it

type rect struct {
    width, height float64
}

func (r rect) area() float64 {
    return r*width*height
}

func (r rect) perim() float64 {
    return 2*r*width + 2*r*height
}

type circle struct {
    radius float64
}

func (c circle) area() float64 {
    return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
    return 2 * math.Pi * c.radius
}

  In this case, we can not compare one to the naked eye to see rect, if the geometry circle implements all the methods defined in it

 

  Go it can be determined by the type of assertion.

func main() {
	r := rect{width: 3, height: 4}
	c := circle{radius: 5}

	measure(r)
	measure(c)
    
	var g geometry
	g = circle{radius:10}
	switch t := g.(type) {
	case circle:
		fmt.Println("circle type", t)
	case rect:
		fmt.Println("rect type", t)
	}
}

  

 

  Implementation of the results

{3 4}
12
14
{5}
78.53981633974483
31.41592653589793
circle type {10}

  As can be seen, Go can be inferred g is achieved circle geometry interface.

 

  The syntax for the type of assertion

<Value target type>, <Boolean parameter>: = <expression> (target type) // security type assertion. 

<Value target type>: = <expression> (target type) // non-security type assertion

  The above wording is switch grammar, that is the second. The first example as follows

var g geometry
if f, ok := g.(circle); ok {
	fmt.Println("circle type", f)
}

   

3, a pointer value of the reception or the reception

  A method in Go, we can define a method is used to receive the value of a pointer to a struct is received, the form

type rect struct {
	width, height int
}

func (r *rect) area() int {
	return r.width * r.height
}

func (r rect) perim() int {
	return 2*r.width + 2*r.height
}

func main() {
	r := rect{width: 10, height: 20}
	fmt.Println("area:", r.area())
	fmt.Println("perim:", r.perim())

	rp := &r
	fmt.Println("area:", rp.area())
	fmt.Println("perim:", rp.perim())
}

  这里定义结构体rect,同时定义两个方法area()和perim()。在这两个方法左边定义的即为方法的接收者,其中area()由rect的指针类型接收,perim()则由rect值类型接收。

  这样表示area()和perim()是rect的两个方法。从代码我们可以看出,该种形式不管是传入值类型还是传入rect的指针,执行都正常返回结果。

area: 200
perim: 60
area: 200
perim: 60

  

对于r.area()可以调通的背后Go做了什么?

  此时r是一个值类型,为了实现调用即使是指针接收类型的area()方法,Go实际是先找到r的地址,然后通过一个指针指向它,即r.area()转化成了(&r).area(),从而满足了area()方法是指针接收者的约束。

 

对于rp.perim()可以调通的背后Go做了什么?

  此时rp是一个指针类型。在调用时,指针被解引用为值,这样便符合perim()方法定义的接收者类型的约束。解引用的过程我们可以认为Go把rp.perim()转化为于(*rp).perim()。但是注意perim()方法是值接收类型,所以操作的是rect的副本。

 

  所以,综上,对于普通方法的调用,不管接收者是值类型还是指针类型,调用者是值类型还是指针类型,都可以调通。

 

  上面是针对纯粹的方法而言的,如果在接口的背景下,情况是否一致呢?

type geometry interface {
	area() float64
	perim() float64
}

type rect struct {
	width, height float64
}

type circle struct {
	radius float64
}

func (r rect) area() float64 {
	return r.width * r.height
}

func (r rect) perim() float64 {
	return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func (c *circle) perim() float64 {
	return 2 * math.Pi * c.radius
}

func measure(g geometry) {
	fmt.Println(g)
	fmt.Println(g.area())
	fmt.Println(g.perim())
}

func main() {
	r := rect{width: 3, height: 4}
	c := circle{radius: 5}

	measure(r)
	measure(c)
}

  

 

  这段的代码与上面的唯一不同的地方在于将perim接收者类型由circle改为了*circle类型,导致在运行程序时报错

# command-line-arguments
main/src/examples/interfaces.go:48:9: cannot use c (type circle) as type geometry in argument to measure:
	circle does not implement geometry (perim method has pointer receiver)

  意思是说circle没有实现geometry接口。

 

  如果反过来

type geometry interface {
	area() float64
	perim() float64
}

type rect struct {
	width, height float64
}

type circle struct {
	radius float64
}

func (r rect) area() float64 {
	return r.width * r.height
}

func (r rect) perim() float64 {
	return 2*r.width + 2*r.height
}

func (c circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func (c *circle) perim() float64 {
	return 2 * math.Pi * c.radius
}

func measure(g geometry) {
	fmt.Println(g)
	fmt.Println(g.area())
	fmt.Println(g.perim())
}

func main() {
	r := &rects1{width: 3, height: 4}
	c := &circle{radius: 5}

	measure(r)
	measure(c)
}

  此时调用一切正常。

  所以对比看下来发现,对于值接收者,传如值或者指针都可以正常调用;对于指针接收者,则只能传入指针类型,否则会报未实现接口的错误。

 

关于原理,我看了很多说法
说法一

"对于指针类型,Go会自动转换,因为有了指针总是能得到指针指向的值是什么,如果是 value 调用,go 将无从得知 value 的原始值是什么,因为 value 是份拷贝。go 会把指针进行隐式转换得到 value,但反过来则不行。  "

 

说法二

"当实现一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。"

  但是这两种说法我觉得还是没有真正说到原理上,也可能是我没有理解。

  在前面不涉及到接口的单纯方法的值接收者和指针接收者,使用值或者指针调用都是可以的,因为Go会在底层做这个类型转换。但是在接口这个背景下,如果方法有指针类型接收类型,则只能传指针类型,可能还是和Go的接口底层实现有关。如果大家有自己的理解,欢迎指教。

 

今天主要介绍了Go语言中的接口的定义和实现以及如何使用,还有一些小知识点比如空interface的作用和使用就不再赘述。

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

 

Guess you like

Origin www.cnblogs.com/bigdataZJ/p/go-interface.html