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
// Define an interface for shapes with a `Area()` method
type Shape interface {
Area() float64
}
Explanation:
- This
Shape
interface defines a single methodArea()
that calculates the area of a shape.
Implementing an Interface for a Struct or Other Type
// Implement the `Shape` interface for a `Circle` struct
type Circle struct {
Radius float64
}
func (c *Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Implement the `Shape` interface for a `Rectangle` struct
type Rectangle struct {
Width, Height float64
}
func (r *Rectangle) Area() float64 {
return r.Width * r.Height
}
Explanation:
- The
Circle
andRectangle
structs implement theShape
interface 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
func PrintInfo(s interface {
Name() string
Info() string
}) {
fmt.Println("Name:", s.Name())
fmt.Println("Info:", s.Info())
}
// Inline implementation for a person struct
type 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
Person
struct can be used withPrintInfo()
even though it doesn't explicitly implement a named interface, demonstrating the flexibility of anonymous interfaces.
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
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
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 method
func (c *Circle) Measure() float64 {
return c.Area() // Reuse the Area()
}