Go > Structs and Interfaces > Interfaces > Interface satisfaction (implicit)

Implicit Interface Satisfaction in Go

This snippet demonstrates how Go achieves interface satisfaction implicitly. A type automatically satisfies an interface if it implements all the methods defined in the interface. No explicit declaration is required.

Defining an Interface

This code defines an interface called Speaker. It has a single method, Speak(), which returns a string. Any type that defines a method named Speak() which returns a string automatically satisfies this interface in Go.

package main

import "fmt"

type Speaker interface {
 Speak() string
}

Implementing the Interface with a Struct

Here, we define a struct called Dog with a Name field. Crucially, we also define a method called Speak() on the Dog type. This method returns a string. Because the Dog type has a method called Speak() that returns a string, it implicitly satisfies the Speaker interface. Note that there is no explicit declaration of the Dog type satisfying the Speaker interface using some implements keyword or something similar. Go determines satisfaction implicitly.

type Dog struct {
 Name string
}

func (d Dog) Speak() string {
 return "Woof! My name is " + d.Name
}

Using the Interface

In the main function, we create an instance of the Dog struct. We then assign this instance to a variable of type Speaker. This is possible because Dog satisfies the Speaker interface. Finally, we call the Speak() method on the speaker variable. The output will be "Woof! My name is Buddy". This showcases how the interface can be used to abstract the concrete type (Dog in this case).

func main() {
 dog := Dog{Name: "Buddy"}

 var speaker Speaker = dog // Implicit conversion

 fmt.Println(speaker.Speak())
}

Complete Code

This is the complete, runnable code example. It demonstrates the definition of an interface, the implementation of that interface by a struct, and the usage of the interface to call a method on the struct instance.

package main

import "fmt"

type Speaker interface {
 Speak() string
}

type Dog struct {
 Name string
}

func (d Dog) Speak() string {
 return "Woof! My name is " + d.Name
}

func main() {
 dog := Dog{Name: "Buddy"}

 var speaker Speaker = dog // Implicit conversion

 fmt.Println(speaker.Speak())
}

Concepts Behind the Snippet

Go's interfaces are a powerful tool for abstraction and polymorphism. The implicit satisfaction of interfaces simplifies development and promotes loose coupling. Types automatically satisfy an interface when they define all of the interface methods with the correct signatures (name, input parameters and return values).

Real-Life Use Case

Consider a system for handling different types of notifications (email, SMS, push). You can define a Notifier interface with a Send() method. Then, you can create different struct types (EmailNotifier, SMSNotifier, PushNotifier) that implement the Send() method. The rest of the system can then interact with these notifiers through the Notifier interface, without needing to know the specific type of notifier being used.

Best Practices

Design interfaces to be small and focused (single responsibility principle). This makes them easier to implement and use. Only define the methods that are absolutely necessary for the interface's purpose. Avoid overly large or complex interfaces.

Interview Tip

Be prepared to explain the concept of implicit interface satisfaction in Go. Highlight the benefits of this approach, such as loose coupling and flexibility. You should be able to show example of how to create interfaces and implement them.

When to Use Interfaces

Use interfaces when you want to achieve polymorphism (the ability to treat objects of different types in a uniform way), decoupling (reducing dependencies between components), and abstraction (hiding implementation details). They are particularly useful when working with collections of objects where the specific types may vary.

Memory Footprint

Using interfaces introduces a small runtime overhead. When an interface variable holds a concrete type, Go stores both the value and type information. However, the benefits of using interfaces (flexibility, testability) often outweigh this small performance cost.

Alternatives

Alternatives to interfaces depend on the desired functionality. For simple use cases, direct implementations may suffice. For more complex scenarios, abstract classes (although Go doesn't directly support classes) can sometimes be emulated using struct embedding.

Pros

  • Loose Coupling: Interfaces decouple code, making it easier to change or extend components without affecting other parts of the system.
  • Polymorphism: Interfaces allow you to treat different types in a uniform way, making your code more flexible and reusable.
  • Testability: Interfaces make it easier to mock dependencies during testing, improving the testability of your code.

Cons

  • Runtime Overhead: Using interfaces introduces a small runtime overhead due to dynamic dispatch.
  • Increased Complexity: Overuse of interfaces can sometimes make your code more complex and harder to understand.

FAQ

  • What happens if a struct doesn't implement all the methods of an interface?

    If a struct doesn't implement all the methods of an interface, it does not satisfy the interface. You cannot assign an instance of that struct to a variable of the interface type, and the compiler will report an error.
  • Can a struct implement multiple interfaces?

    Yes, a struct can implement multiple interfaces. It simply needs to implement all the methods defined by each interface. Go's implicit interface satisfaction makes this straightforward.
  • Are interfaces similar to abstract classes?

    Interfaces are similar to abstract classes in that they define a contract that implementing types must adhere to. However, interfaces in Go only define method signatures, not any implementation details or fields (before Go 1.18, which introduced type embedding). Abstract classes, in some languages, can contain both abstract and concrete methods.