YouTip LogoYouTip

Go Interfaces

An interface (interface) is a type in the Go language used to define a set of behaviors. It specifies a type's behavioral contract by describing the methods that the type must implement. Go provides another data type called an interface, which defines all methods with common characteristics together. Any other type that implements these methods is considered to have implemented this interface. Go's interface design is simple yet powerful, serving as an important tool for implementing polymorphism and decoupling. Interfaces allow us to bind different types to a set of common methods, thereby achieving polymorphism and flexible design. ### Characteristics of Interfaces **Implicit Implementation**: * There is no keyword in Go to explicitly declare that a type implements a certain interface. * As long as a type implements all the methods required by an interface, that type is automatically considered to have implemented the interface. **Interface Type Variables**: * An interface variable can store any value that implements the interface. * An interface variable actually contains two parts: * **Dynamic Type**: Stores the actual type of the value. * **Dynamic Value**: Stores the concrete value. **Zero Value of an Interface**: * The zero value of an interface is `nil`. * An uninitialized interface variable has a value of `nil` and does not contain any dynamic type or value. **Empty Interface**: * Defined as `interface{}`, it can represent any type. ### Common Uses of Interfaces 1. **Polymorphism**: Different types implement the same interface to achieve polymorphic behavior. 2. **Decoupling**: Define dependencies through interfaces to reduce coupling between modules. 3. **Generalization**: Use the empty interface `interface{}` to represent any type. * * * ## Interface Definition and Implementation An interface is defined using the `interface` keyword, which contains method declarations. ## Example /* Define an interface */ type interface_name interface{ method_name1 method_name2 method_name3 ... method_namen } /* Define a struct */ type struct_name struct{ /* variables */ } /* Implement interface methods */ func(struct_name_variable struct_name) method_name1(){ /* method implementation */ } ... func(struct_name_variable struct_name) method_namen(){ /* method implementation */ } **Define a simple interface:** type Shape interface { Area() float64 Perimeter() float64 } * `Shape` is an interface that defines two methods: `Area` and `Perimeter`. * Any type that implements these two methods is considered to have implemented the `Shape` interface. **Implementing an interface:** A type implements an interface by implementing all the methods required by the interface. ## Example package main import( "fmt" "math" ) // Define an interface type Shape interface{ Area()float64 Perimeter()float64 } // Define a struct type Circle struct{ Radius float64 } // Circle implements the Shape interface func(c Circle) Area()float64{ return math.Pi * c.Radius * c.Radius } func(c Circle) Perimeter()float64{ return 2* math.Pi * c.Radius } func main(){ c := Circle{Radius:5} var s Shape = c // Interface variable can store a type that implements the interface fmt.Println("Area:", s.Area()) fmt.Println("Perimeter:", s.Perimeter()) } Executing the above code, the output is: Area: 78.53981633974483Perimeter: 31.41592653589793 * * * ## Empty Interface The empty interface `interface{}` is a special interface in Go, representing the superset of all types. * Any type implements the empty interface. * It is commonly used in scenarios where data of any type needs to be stored, such as generic containers, universal parameters, etc. ## Example package main import"fmt" func printValue(val interface{}){ fmt.Printf("Value: %v, Type: %Tn", val, val) } func main(){ printValue(42)// int printValue("hello")// string printValue(3.14)// float64 printValue([]int{1,2})// slice } Executing the above code, the output is: Value: 42, Type: intValue: hello, Type: stringValue: 3.14, Type: float64 Value: , Type: []int * * * ## Type Assertion Type assertion is used to extract the underlying value from an interface type. Basic syntax: value := iface.(Type) * `iface` is the interface variable. * `Type` is the concrete type to assert. * If the types do not match, it will trigger a `panic`. ## Example package main import"fmt" func main(){ var i interface{}="hello" str :=i.(string)// Type assertion fmt.Println(str)// Output: hello } ### Checked Type Assertion To avoid panic, you can use a checked type assertion: value, ok := iface.(Type) * `ok` is a boolean value indicating whether the assertion was successful. * If the assertion fails, `value` is the zero value, and `ok` is `false`. ## Example package main import"fmt" func main(){ var i interface{}=42 if str, ok :=i.(string); ok { fmt.Println("String:", str) }else{ fmt.Println("Not a string") } } Executing the above code, the output is: Not a string * * * ## Type Switch `type switch` is a syntactic structure in Go used to execute different logic based on the concrete type of an interface variable. ## Example package main import"fmt" func printType(val interface{}){ switch v := val.(type){ case int: fmt.Println("Integer:", v) case string: fmt.Println("String:", v) case float64: fmt.Println("Float:", v) default: fmt.Println("Unknown type") } } func main(){ printType(42) printType("hello") printType(3.14) printType([]int{1,2,3}) } Executing the above code, the output is: Integer: 42String: hello Float: 3.14Unknown type * * * ## Interface Composition Interfaces can be composed through embedding to achieve more complex behavioral descriptions. ## Example package main import"fmt" type Reader interface{ Read()string } type Writer interface{ Write(data string) } type ReadWriter interface{ Reader Writer } type File struct{} func(f File) Read()string{ return"Reading data" } func(f File) Write(data string){ fmt.Println("Writing data:", data) } func main(){ var rw ReadWriter = File{} fmt.Println(rw.Read()) rw.Write("Hello, Go!") } * * * ## Dynamic Value and Dynamic Type An interface variable actually contains two parts: 1. **Dynamic Type**: The concrete type stored in the interface variable. 2. **Dynamic Value**: The value of the concrete type. Example of dynamic value and dynamic type: ## Example package main import"fmt" func main(){ var i interface{}=42 fmt.Printf("Dynamic type: %T, Dynamic value: %vn", i, i) } Executing the above code, the output is: Dynamic type: int, Dynamic value: 42 * * * ## Zero Value of an Interface The zero value of an interface is `nil`. When both the dynamic type and dynamic value of an interface variable are `nil`, the interface variable is `nil`. Example of interface zero value: ## Example package main import"fmt" func main(){ var i interface{} fmt.Println(i==nil)// Output: true } * * * ## Practice Examples The following two examples demonstrate the use of interfaces: ## Example 1 package main import( "fmt" ) type Phone interface{ call() } type NokiaPhone struct{ } func(nokiaPhone NokiaPhone) call(){ fmt.Println("I am Nokia, I can call you!") } type IPhone struct{ } func(iPhone IPhone) call(){ fmt.Println("I am iPhone, I can call you!") } func main(){ var phone Phone phone =new(NokiaPhone) phone.call() phone =new(IPhone) phone.call() } In the above example, we defined an interface **Phone**, which has a method `call()`. Then, in the **main** function, we defined a variable of type **Phone** and assigned it to **NokiaPhone** and **IPhone** respectively. Then, we called the `call()` method, and the output is as follows: I am Nokia, I can call you! I am iPhone, I can call you! Second interface example: ## Example package main import"fmt" type Shape interface{ area()float64 } type Rectangle struct{ width float64 height float64 } func(r Rectangle) area()float64{ return r.width * r.height } type Circle struct{ radius float64 } func(c Circle) area()float64{ return 3.14* c.radius * c.radius } func main(){ var s Shape s = Rectangle{width:10, height:5} fmt.Printf("Rectangle area: %fn", s.area()) s = Circle{radius:3} fmt.Printf("Circle area: %fn", s.area()) } In the above example, we defined a `Shape` interface, which defines a method `area()` that returns a `float64` area value. Then, we defined two structs, `Rectangle` and `Circle`, which respectively implemented the `area()` method of the `Shape` interface. In the `main()` function, we first defined a variable `s` of type `Shape`, then assigned instances of `Rectangle` and `Circle` to it, and calculated their areas by calling the `area()` method and printed them. The output is as follows: Rectangle area: 50.000000Circle area: 28.260000 It should be noted that an interface type variable can store the value of any type that implements that interface. In the example, we assigned instances of both `Rectangle` and `Circle` types to the `Shape` type variable `s` and called their area calculation methods through the `area()` method.
← Go IdeGo Map β†’