This is the 20th article in the “Learn to Go” series

Reminder: at the end of the article for you to leave a small exercise, you can first read the article, and then do exercises, test their learning results!

Let’s pick up where we left off and move on to other uses of interfaces.

Implementing multiple interfaces

A type can implement multiple interfaces. Here’s an example:

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area(a) float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter(a) float32 {
	return 2 * math.Pi * c.radius
}

func main(a) {
	c := Circle{3}
	var s Shape = c
	var p Object = c
	fmt.Println("area: ", s.Area())
	fmt.Println("perimeter: ", p.Perimeter())
}
Copy the code

Output:

area:  28.274334
perimeter:  18.849556
Copy the code

In the above code, the structure Circle implements the Shape interface and the Object interface respectively, so we can assign the structure variable C to the variables S and P, where S and P have the same dynamic type and dynamic value. Call their respective implemented methods Area() and Perimeter(), respectively. Let’s modify the program:

fmt.Println("area: ", p.Area())
fmt.Println("perimeter: ", s.Perimeter())
Copy the code

Compilation error:

p.Area undefined (type Object has no field or method Area)
s.Perimeter undefined (type Shape has no field or method Perimeter)
Copy the code

Why is that? Because the static type of S is Shape and the static type of P is Object. Is there a solution? Yes, let’s move on to the next section

Types of assertions

Type assertions can be used to get the underlying value of an interface, usually in the syntax I.(Type), where I is the interface and Type is the Type or interface. The dynamic Type of I is automatically checked for consistency with Type at compile time.

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area(a) float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter(a) float32 {
	return 2 * math.Pi * c.radius
}

func main(a) {
	var s Shape = Circle{3}
	c := s.(Circle)
	fmt.Printf("%T\n",c)
	fmt.Printf("%v\n",c)
	fmt.Println("area: ", c.Area())
	fmt.Println("perimeter: ", c.Perimeter())
}
Copy the code

Output:

main.Circle
{3}
area:  28.274334
perimeter:  18.849556
Copy the code

The code above solves this problem by using C to access the underlying values of interface S and by calling the methods Area() and Perimeter(), respectively. In syntax I.(Type), if Type does not implement the interface to which I belongs, an error will be reported at compile time. Or if the dynamic value of I is not Type, a panic error is reported. How do you solve it? You can use the following syntax:

value, ok := i.(Type)
Copy the code

Using the syntax above, Go will automatically detect the two cases mentioned above, and we just need the variable OK to determine whether the result is correct. Ok is true if correct, false otherwise, and value is the zero value corresponding to Type.

Type selection

Type selection is used to match and compare the specific type of an interface with multiple types specified in various case statements, similar to switch case statements, except that the type is specified in case statements. The syntax for type selection is somewhat similar to the syntax for type assertion: I.(type), where I is the interface and type is the fixed keyword. This is used to obtain the specific type of the interface rather than the value.

func switchType(i interface{}) {
	switch i.(type) {
	case string:
		fmt.Printf("string and value is %s\n", i.(string))
	case int:
		fmt.Printf("int and value is %d\n", i.(int))
	default:
		fmt.Printf("Unknown type\n")}}func main(a) {
	switchType("Seekload")
	switchType(27)
	switchType(true)}Copy the code

Output:

string and value is Seekload
int and value is 27
Unknown type
Copy the code

The above code should be easy to understand. In which case the type of I matches, the corresponding output statement will be executed. Note: Only the interface type can be selected. Other types, such as int, string, etc., cannot:

i := 1
switch i.(type) {
case int:
	println("int type")
default:
	println("unknown type")}Copy the code

Error:

cannot type switch on non-interface value i (type int)
Copy the code

Nested interface

In Go, interfaces cannot be implemented or inherited from other interfaces, but new interfaces can be created by nesting them.

type Math interface {
	Shape
	Object
}
type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area(a) float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter(a) float32 {
	return 2 * math.Pi * c.radius
}

func main(a) {

	c := Circle{3}
	var m Math = c
	fmt.Printf("%T\n", m )
	fmt.Println("area: ", m.Area())
	fmt.Println("perimeter: ", m.Perimeter())
}
Copy the code

Output:

main.Circle
area:  28.274334
perimeter:  18.849556
Copy the code

The above code creates a new interface Math by nesting the Shape and Object interfaces. Any type that implements the methods defined by the Shape and Object interfaces also implements the Math interface, such as the structure Circle we created. Inside the main function, the variable m defines the type of the interface. The dynamic type is the Perimeter structure. Note that the Area and Perimeter methods are called in such a way that members of nested structures are accessed.

Implement interfaces using pointer receivers and value receivers

While we implemented the interface through the value receiver, we can also implement the interface through the pointer receiver. There are still some points to note in the implementation process, let’s take a look:

type Shape interface {
	Area() float32
}

type Circle struct {
	radius float32
}

type Square struct {
	side float32
}

func (c Circle) Area(a) float32 {
	return math.Pi * (c.radius * c.radius)
}

func (s *Square) Area(a) float32 {
	return s.side * s.side
}

func main(a) {
	var s Shape
	c1 := Circle{3}
	s = c1
	fmt.Printf("%v\n",s.Area())

	c2 := Circle{4}
	s = &c2
	fmt.Printf("%v\n",s.Area())

	c3 := Square{3}
	//s = c3
	s = &c3
	fmt.Printf("%v\n",s.Area())

}
Copy the code

Output:

28.274334
50.265484
9
Copy the code

In the code above, the structure Circle implements the interface Shape via the value receiver. As we discussed in the methods article, the value receiver’s methods can be called using values or Pointers, so the above c1 and C2 calls are valid.

The Square structure implements the Shape interface through the pointer receiver. If you open the comments section above, the compilation will fail:

cannot use c3 (type Square) as type Shape in assignment:
Square does not implement Shape (Area method has pointer receiver)
Copy the code

It is clear from the error message that we are trying to assign the value type C3 to S, but c3 does not implement Shape. This may come as a bit of a surprise, because in a method we can call the pointerreceiver method directly from a value type or a pointer type. Remember that it is legal to call a pointer or an addressable value for a pointer recipient method. However, the actual value stored by the interface is not addressable, and the compiler cannot automatically obtain the address of C3, so the program reports an error.

This concludes the use of interfaces, and I hope these two articles can help you!

Assignment: The article mentioned the Type assertion: I.(Type), where I is an interface and Type can be a Type or interface. If Type is an interface, what does the expression mean? What does the following program output?

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area(a) float32 {
	return math.Pi * (c.radius * c.radius)
}

func main(a) {
	var s Shape = Circle{3}
	value1,ok1 := s.(Shape)
	value2,ok2 := s.(Object)

	fmt.Println(value1,ok1)
	fmt.Println(value2,ok2)
}
Copy the code

Welcome to leave comments and discuss!


Original article, if need to be reproduced, please indicate the source! Check out “Golang is coming” or go to seekload.net for more great articles.

The public account “Golang is coming” has prepared a mystery learning gift package for you, and the background replies [ebook] to get it!