Interfaces in Go
Go interfaces offer a powerful tool for promoting abstraction, code reusability, and modularity. They define a set of method signatures that different types can implement, enabling you to treat objects with similar behaviors uniformly.
Defining an Interface with Method Signatures
Section titled “Defining an Interface with Method Signatures”// Define an interface for shapes with a `Area()` methodtype Shape interface { Area() float64}Explanation:
- This
Shapeinterface defines a single methodArea()that calculates the area of a shape.
Implementing an Interface for a Struct or Other Type
Section titled “Implementing an Interface for a Struct or Other Type”// Implement the `Shape` interface for a `Circle` structtype Circle struct { Radius float64}
func (c *Circle) Area() float64 { return math.Pi * c.Radius * c.Radius}
// Implement the `Shape` interface for a `Rectangle` structtype Rectangle struct { Width, Height float64}
func (r *Rectangle) Area() float64 { return r.Width * r.Height}Explanation:
- The
CircleandRectanglestructs implement theShapeinterface by defining their ownArea()methods based on their respective formulas. - Both structs now have access to the
Area()method, promoting code reuse.
Anonymous Interfaces and Inline Implementations
Section titled “Anonymous Interfaces and Inline Implementations”func PrintInfo(s interface { Name() string Info() string}) { fmt.Println("Name:", s.Name()) fmt.Println("Info:", s.Info())}
// Inline implementation for a person structtype Person struct { Name string}
func (p Person) Info() string { return "Just a regular person"}
func main() { p := Person{Name: "Alice"} PrintInfo(p) // Works because Person implements the anonymous interface}Explanation:
- An anonymous interface defines methods within the function itself, allowing flexible use without explicit interface definitions.
- The
Personstruct can be used withPrintInfo()even though it doesn’t explicitly implement a named interface, demonstrating the flexibility of anonymous interfaces.
Implicit Interface Satisfaction
Section titled “Implicit Interface Satisfaction”type Writer interface { Write(p []byte) (n int, err error)}
type MyWriter struct{}
func (mw MyWriter) Write(p []byte) (int, error) { // Implementation for writing data return len(p), nil}
func main() { var w Writer = MyWriter{} // Implicitly satisfies Writer because MyWriter has the required method _, err := w.Write([]byte("Hello")) if err != nil { // Handle error }}Explanation:
- Any type that has a method with the same signature as an interface method implicitly satisfies that interface.
- This allows for simpler code and flexibility when types naturally provide the required behavior.
Interface Values and Type Assertion
Section titled “Interface Values and Type Assertion”func PrintValue(v interface{}) { switch t := v.(type) { case int: fmt.Println("Integer:", t) case string: fmt.Println("String:", t) case float64: fmt.Println("Float64:", t) default: fmt.Println("Unknown type:", reflect.TypeOf(t)) }}
func main() { values := []interface{}{10, "Hello", 3.14} for _, v := range values { PrintValue(v) }}Explanation:
- Interface values can hold any type that implements the interface’s methods.
- Type assertion (
t := v.(type)) allows you to check the concrete type of an interface value and access its specific methods. - Be cautious with type assertions to avoid runtime errors if the value doesn’t match the expected type.
Embedding Interfaces
Section titled “Embedding Interfaces”type Measurable interface { Measure() float64}
type Shape interface { Area() float64}
type Circle struct { Radius float64}
func (c *Circle) Area() float64 { return math.Pi * c.Radius * c.Radius}
// Circle implicitly satisfies Measurable because it has the required methodfunc (c *Circle) Measure() float64 { return c.Area() // Reuse the Area()
}