Interfaces
Structural vs nominal typing
Introduction
In this lesson, you'll learn about interfaces in Go. Coming from Java, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In Java, you're familiar with structural vs nominal typing.
Go has its own approach to structural vs nominal typing, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
package main
// Structural typing — no "implements" needed
type Stringer interface {
String() string
}
type Dog struct{ Name string }
// Dog satisfies Stringer automatically
func (d Dog) String() string { return "Dog: " + d.Name }
type Cat struct{ Name string }
func (c Cat) String() string { return "Cat: " + c.Name }
func print(s Stringer) {
fmt.Println(s.String())
}
print(Dog{"Rex"}) // works
print(Cat{"Whiskers"}) // works tooComparing to Java
Here's how you might have written similar code in Java:
// Nominal typing — must declare "implements"
public interface Stringer {
String toString();
}
public class Dog implements Stringer { // explicit
private String name;
public Dog(String name) { this.name = name; }
@Override
public String toString() { return "Dog: " + name; }
}
// Must implement all interface methods
// Or get a compile errorYou may be used to different syntax or behavior.
Go uses structural typing — implement the methods, satisfy the interface
You may be used to different syntax or behavior.
Java requires explicit 'implements'; Go needs no declaration
You may be used to different syntax or behavior.
Go interfaces are typically small (1-2 methods); Java can have many
You may be used to different syntax or behavior.
Go empty interface{} (or any) accepts any type; Java uses Object
Step-by-Step Breakdown
1. Structural vs Nominal
Java requires explicit 'implements Interface'. Go just checks if the type has the required methods — no declaration needed.
public class Dog implements Stringer { ... }type Dog struct{ Name string }
func (d Dog) String() string { return "Dog: " + d.Name }
// Dog automatically satisfies Stringer2. Small Interfaces
Go interfaces are often just 1 method — io.Reader has Read(), fmt.Stringer has String(). Java interfaces tend to be larger.
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type ReadWriter interface { Reader; Writer } // composed3. Empty Interface
Go's empty interface (interface{} or any) accepts any value — like Java's Object. Use with caution.
void process(Object obj) { ... }func process(obj any) { ... } // any = interface{}Common Mistakes
When coming from Java, developers often make these mistakes:
- Go uses structural typing — implement the methods, satisfy the interface
- Java requires explicit 'implements'; Go needs no declaration
- Go interfaces are typically small (1-2 methods); Java can have many
Key Takeaways
- Go structural typing: no 'implements' declaration
- Go interfaces are typically tiny (1-2 methods)
- interface{} / any = Java Object
- Composed interfaces via embedding