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!