What are some examples of Go interfaces?
I found an interesting blog post about Go.
I am trying to understand the concept of interfaces, but I find it very hard to do so from the code fragment in the blog post, and nearly impossible from the language specification.
Can anyone point out a simple example of Go's interfaces in a working program?
It's a work-in-progress learning exercise, and certainly a poor example of good style, but here you go (spec).
Additionally, as a more exotic example, I made a post on the go-nuts mailing list regarding using interface{} for building functions that work with anonymous data (in this case, a "ternary operation" function):
package main
import "fmt";
func Tern(exp bool, a interface{}, b interface{}) (interface{}) {
if exp { return a }
return b
}
func main() {
a := 7; b := 1;
result := Tern(a > b, a, b);
fmt.Printf("%d\n", result);
}
The tutorial "Interfaces in Go - Part 2: Aiding adaptable, evolutionary design " (January 2012, from Sathish VJ) mentions clearly the main advantage for interfaces in Go:
Go's interfaces aren't a variant on Java or C# interfaces, they're much more.
They are a key to large-scale programming and adaptable, evolutionary design.
See this example, from the same article, about different perspective (interface) for a Bus:
package main
import "fmt"
//Go Step 1: Define your data structures
type Bus struct {
l, b, h int
rows, seatsPerRow int
}
//Go Step 2: Define a real world abstraction that could use the data we structure we have
type Cuboider interface {
CubicVolume() int
}
//Go Step 3: Implement methods to work on data
func (bus Bus) CubicVolume() int {
return bus.l * bus.b * bus.h
}
//Go step - repeat 2 & 3 for any other interfaces
type PublicTransporter interface {
PassengerCapacity() int
}
func (bus Bus) PassengerCapacity() int {
return bus.rows * bus.seatsPerRow
}
func main() {
b := Bus{
l:10, b:6, h:3,
rows:10, seatsPerRow:5}
fmt.Println("Cubic volume of bus:", b.CubicVolume())
fmt.Println("Maximum number of passengers:", b.PassengerCapacity())
}
It appears to be data centric - define your data first and build your interface abstractions as you go along.
Hierarchy here is kind of built 'along the way' without explicitly stating it - depending on the method signatures associated with the type, it is understood as implementing specific interfaces.Let us assume now that as time evolved, some of the project requirements for our Bus changed - there is now a new law that says that each passenger should at least have a certain minimum amount of cubic volume.
Our Bus now now has to adhere to a new interface calledPersonalSpaceLaw
which is distinct from any of the other interfaces it already implements
//new requirement that the Bus must be compatible with
type PersonalSpaceLaw interface {
IsCompliantWithLaw() bool
}
func (b Bus) IsCompliantWithLaw() bool {
return (b.l * b.b * b.h) / (b.rows * b.seatsPerRow) >= 3
}
The functionality has been extended without any change to the core classes or core hierarchies. This implementation is much cleaner, easily extensible, and can scale better with the changing needs of the project's requirements.
Here is the full working program in Go Playground
The article ends with John Asmuth's quote from from the thread about the productivity of interfaces in Go:
"It's the fact that I don't have to spend time up front designing some sort of type hierarchy and then rearranging it two or three times before I finish.
It's not even the fact that it's easy to do it right -
it's the fact that I just don't have to worry about it and can get on with the actual algorithm."
package main
type Stringer interface {
String() string
}
type pie int
type pizza string
func (p pie) String() string{
return "pie"
}
func (p pizza) String() string{
return "pizza"
}
func main(){
var a pie
var b pizza
fmt.Println(a,b) //fmt.Println() will look for Stringers and call their String() method.
}
Extending @Jessta excellent example. A simple example of the use of Go's' interface in a working program to access Go's standard library is given.
package main
import (
"encoding/json"
. "fmt"
)
func main() {
var i interface{} = c
e := func() error { return c } // type error interface { Error() string}
Println(e()) // Hiss
s := func() Stringer { return d } // type Stringer interface {String() string}
// func Println(a ...interface{}) (n int, err error)
Println(s()) // Woof
d := `{"Pet":"Dog","Age":2, "Eat": "Bone"}`
json.Unmarshal([]byte(d), &i) // func Unmarshal(data []byte, v interface{}) error
m := i.(map[string]interface{})
Println(m["Age"]) // 2
}
type cat string
type dog string
var c cat
var d dog
func (cat) Error() string { return "Hiss" }
func (dog) String() string { return "Woof" }
Interfaces are Go's most distinctive and powerful feature. They have a profound effect on library design. They enable true component architectures. Prime examples are io.Reader and io.Writer, generalizations of the Unix pipe idea. See https://talks.golang.org/2015/simplicity-is-complicated.slide.
By convention, errors have type error, a simple built-in interface. See https://golang.org/doc/effective_go.html#errors and https://talks.golang.org/2012/splash.article. An error variable represents any value that can describe itself as a string. func() error { return c }
calls type error interface { Error() string}
. func (cat) Error() string
implements type error interface { Error() string}
. See https://blog.golang.org/error-handling-and-go.
A Stringer can pretty print itself. Anything that implements String is a Stringer. fmt.Println
calls the String method if the parameter is a Stringer. See https://talks.golang.org/2013/go4python.slide#33. func() Stringer { return d }
calls type Stringer interface {String() string}
. func (dog) String() string
implements type Stringer interface {String() string}
.
The signature of fmt.Println
is func Println(format string, a ...interface{}) (n int, err error)
which is to say its arguments (after the format string) are interface values. See https://blog.golang.org/constants. Quotes edited to match example.
Pervasive use of key interfaces in the standard library make it easy to chain APIs together. See https://talks.golang.org/2012/goforc.slide#46. See https://talks.golang.org/2014/go4gophers and https://talks.golang.org/2014/go4gophers.slide#1 for more examples.