Hi, I am Xingzhou, today we learn Go language interface.

Like Channel and coroutines, Go’s interface design is also a major feature. Unlike Java, C++, PHP, etc., where a class must explicitly declare that it implements an interface, Go considers a type to implement the interface as long as it implements all of the methods in the interface. This looser implementation makes object-oriented programming much easier.

The statement interface

typeThe name of the interfaceinterface{method one (passed argument) (return value) Method two (passed argument) (return value)}Copy the code

Declare an interface with the type interface name interface, followed by curly braces defining the methods needed to implement the interface. Go’s interface has only methods and no attributes. Any type that implements all methods can be considered implementing this interface, also known as Duck typing.

Duck typing: How to judge whether an animal is a Duck? If an animal walks, quacks, has feathers and looks like a duck, we think it is a duck. By extension, an object’s type is determined by its appearance and behavior.

Basic usage

Implementing an interface

Case 1:

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge()
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a)  {
   fmt.Printf("I am %d years old!",j.age)
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d Dog= a1 // declare an instance d of type Dog and assign a1 to d
   d.getAge()// print: I am 2 years old!
}
Copy the code

In the example above, we declared the Dog interface, which contains only the getAge() method. We then define the Jinmao structure, and Jinmao also implements the getAge method, so we can say that Jinmao implements the Dog interface. We define a1 as type Jinmao, and then D as type Dog. Since a1 is a type that implements the Dog interface, we can assign a1 to D, and d will call the method implemented by Jinmao when getAge is called.

One key point to note in the example is that the getAge method implemented by the Jinmao structure not only has the same name as Dog’s getAge method, but also has to have exactly the same parameters and return values for Jinmao to be considered to implement the getAge method defined by the Dog interface.

The zero value of an interface type in Go is nil.

Interface effect

In example 1, we implemented the interface, but it didn’t show the value of using it. Example 2:

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a) int {
   return j.age
}

type Husky struct {
   age int
}

func (h Husky) getAge(a) int {
   return h.age
}

// Calculate the ages of all 🐶🐶 and
func AddAge(s []Dog) int {
   var sum int
   for _,v := range s{
      sum += v.getAge()
   }
   return sum
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d

   a2 := Husky{age: 3}
   var d2 Dog = a2 // declare instance d2 of type Dog and assign a2 to d

   ageSum := AddAge([]Dog{d1,d2})
   fmt.Printf("Sum of dog ages: %d", ageSum)
}
Copy the code

In the example above, we declared Jinmao and Husky and both implemented the Dog interface. The AddAge method receives slices with elements of type Dog. We declare a1 to be of type Jinmao and assign to D1 and a2 to be of type Husky and assign to D2. Next, when you call the AddAge method, you iterate through each element of type Dog inside the AddAge method and call the getAge method, which executes its own method based on the actual type it was given.

The actual type is the type assigned to the actual object that implements the interface. For example, var d1 Dog= a1 in example 2. The type of D1 is the type to which A1 (the actual object) belongs. A1 belongs to Jinmao, so Jinmao is the actual type of D1.

Continuing with the example above, we added the Teddy type and implemented the Dog interface. When we call the AddAge method, nothing is changed inside the method. That’s what interfaces do for us.

The interface type

Example 3:

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a) int {
   return j.age
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d
   fmt.Printf("The type of d1 is: %T", d1) // the type of d1 is main.jinmao
}
Copy the code

In the Printf method of the FMT package, %T represents the type d1. The actual type of the final output D1 is Jinmao.

Empty interface

We use interface{} to denote an empty interface. According to the definition of interface in Go, objects that implement interface methods are implementation types of the interface. Empty interfaces do not define methods, which means that all types implement empty interfaces.

Interface {} is used when a method can accept arguments of any type.

Advanced usage

The interface type

Example 4:

// Declare the Dog interface, including the age method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a) int {
   return j.age
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d
   v,ok := d1.(Jinmao) // Check whether the actual type of d1 is Jinmao
   fmt.Printf("v=%v,ok=%v",v,ok) // print v={2},ok=true
}
Copy the code

In the example above, we determine the actual type of D1 by v, OK := d1.(Jinmao). A more common approach is to determine the actual type of data through a Switch case statement. Example 5:

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a) int {
   return j.age
}

type Husky struct {
   age int
}

func (h Husky) getAge(a) int {
   return h.age
}

func assertType(i interface{}){
   switch i.(type) {
   case Jinmao:
      fmt.Println("The actual type is Jinmao")
   case Husky:
      fmt.Println("The actual type is Husky")
   case int:
      fmt.Println("The actual type is int")
   default:
      fmt.Println("Type not matched")}}func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d

   assertType(a1) // print the actual type is Jinmao
   assertType(d1) // print the actual type is Jinmao
   assertType(100// print the actual type is int
   assertType("str"// print does not match the type
}
Copy the code

In Example 5, we can see how to determine the actual type of a variable using the switch I.(type){} statement.

Value type and pointer type methods

When we talked about methods, we talked about methods that have value types and methods that have pointer types. How do these two types of methods implement interfaces differently? Example 6:

// Declare the Dog interface, including the age method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j Jinmao) getAge(a) int {
   return j.age
}

type Husky struct {
   age int
}

func (h *Husky) getAge(a) int {
   return h.age
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d1

   a2 := Jinmao{age:3}
   var d2 Dog= &a2 // declare instance d1 of type Dog and assign a1 to d2

   //a3 := Husky{age:2}
   //var d3 Dog= a3 // declare instance d1 of type Dog and assign a1 to d3

   a4 := Husky{age:4}
   var d4 Dog= &a4 // declare instance d1 of type Dog and assign a1 to d4

   fmt.Println(d1.getAge()) // print 2
   fmt.Println(d2.getAge()) // print 3
   // fmt.Println(d3.getAge())
   fmt.Println(d4.getAge()) // print 4
}
Copy the code

In the above example, the value type of Jinmao implements getAge(), a1 is the value type of Jinmao, & A2 is the pointer type of Jinmao, and both A1 and A2 can be assigned to a value of type Dog. The pointer type for Husky implements getAge(),a3 is the value type for Husky, and &A4 is the pointer type for Husky. Only &A4 can assign Dog d4. A compile-time error is reported if A3 is assigned to D3. Type does not implement ‘Dog’ as ‘getAge’ method has a pointer receiver. This is a feature of the Go language value type methods and pointer type methods. We can understand that object value type methods also automatically include pointer type methods; Pointer methods do not contain value methods.

Methods of object value type also automatically include methods of pointer type because Go was optimized at compile time.

Nested interface

Interfaces can be nested to achieve an effect similar to inheritance. Example 7:

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge() int
}

// Declare the Cat interface, including the getName method
type Cat interface {
   getName() string
}
// Animal interface, nested Dog and Cat interface
type Animal interface {
   Dog
   Cat
}

type Husky struct {
   age int
   name string
}

func (h Husky) getAge(a) int {
   println("I am Husky getAge!")
   return h.age
}

func (h Husky) getName(a) string {
   println("I am Husky getName!")
   return h.name
}

type OrangeCat struct {
   age int
   name string
}

func (o OrangeCat) getAge(a) int {
   println("I am OrangeCat getAge!")
   return o.age
}

func (o OrangeCat) getName(a) string {
   println("I am OrangeCat getName!")
   return o.name
}

func main(a)  {
   h1 := Husky{age:2,name:"xiaohei"}
   var a1 Animal= h1 // declare instance d1 of type Dog and assign a1 to d1

   o1 := OrangeCat{age:1, name:"Tom"}
   var a2 Animal= o1 // declare instance d1 of type Dog and assign a1 to d2

   fmt.Println(a1.getAge())
   fmt.Println(a1.getName())
   fmt.Println(a2.getAge())
   fmt.Println(a2.getName())
}
Copy the code

Execute the code for example 6 with the following output:

I am Husky getAge!
2
I am Husky getName!
xiaohei
I am OrangeCat getAge!
0
I am OrangeCat getName!
Tom
Copy the code

In example 6 we declare that the Animal interface contains Dog and Cat interfaces. To implement the Animal interface, we need to implement all the methods of Dog and Cat interfaces. We implement getAge and getName methods for both the Husky and OrangeCat struct types, so a1 and A2 can be assigned by H1 and o1.

Connection KouFu values

Interface assignment differs for value types and pointer types, as shown in the following example. Example 8

// Declare the Dog interface, including the getAge method
type Dog interface{
   getAge() int
}

type Jinmao struct {
   age int
}

func (j *Jinmao) setAge(i int) {
   j.age = i
}

func (j Jinmao) getAge(a) int {
   return j.age
}

func main(a)  {
   a1 := Jinmao{age:2}
   var d1 Dog= a1 // declare instance d1 of type Dog and assign a1 to d1
   a1.setAge(20)
   fmt.Printf("a1.age=%d \n",a1.getAge())
   fmt.Printf("d1.age=%d \n",d1.getAge())

   a2 := Jinmao{age:3}
   var d2 Dog= &a2 // declare an instance of type Dog d2 and assign a2 to d2
   a2.setAge(30)
   fmt.Printf("a1.age=%d \n",a2.getAge())
   fmt.Printf("a1.age=%d \n",d2.getAge())
}
Copy the code

Executing the code in Example 8 above produces the following output:

a1.age=20 
d1.age=2 
a1.age=30 
a1.age=30 
Copy the code

Calling setAge on A1 does not change the age of d1. Calling the setAge method on A2 changes the age of D2. In Go, assignment operations copy values, but D2 copies the address of A2, so d2 and A2 refer to the same memory address. When a2’s age changes, d2’s age also changes.

conclusion

In this paper, we mainly introduce the basic usage of Go language interface, nested use, assignment principle, etc. Interfaces feature prominently in the Go language and are widely used in object-oriented programming. If you have any questions or suggestions about the content of this article, you are welcome to exchange private messages.

I put the Go language basic knowledge related articles Demo into the following warehouse, convenient for everyone to exchange learning. Github.com/jialj/golan…

Invite attention to the public number: a row of weekly update technical articles, and we progress together.