Golang interfaces: Difference between revisions

From wikinotes
No edit summary
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
Similar to other languages, interfaces in go define a contract of method-signatures that implementors must have.<br>
Similar to other languages, interfaces in go define a contract of method-signatures that implementors must have.<br>
Unlike other languages, golang interfaces are implicit -- an object with all of the required methods automatically satisfies an interface.
Unlike other languages, golang interfaces are implicit -- an object with all of the required methods automatically satisfies an interface.<br>
In go, it is encouraged to keep very small interfaces (ex. 1 method). The larger the interface, the weaker the abstraction.
 
Similar to other languages, a param can be typed to accept an interface, which abstracts the actual type that is received.
 
{{ NOTE |
go interfaces define a contract of '''methods''', but not '''struct fields'''.<br>
you'll need to define getters/setters for fields }}


= Basics =
= Basics =
<blockquote>
<blockquote>
In go, many interfaces expose just a single method, and are named after the method they expose.<br>
Libraries do not need to expose interfaces in go, you can create them for the subset of methods that are useful to you.
Frequently, interfaces are implemented on structs (but they can be for any type).
 
* An interface is a promise that an object implements a set of method-signatures.
* objects are automatically a part of an interface if they have methods, there is no <code>implements Foo</code>
* Empty interfaces match all oject types in go. (ex <code>fmt.Println()</code> can accept any type)
 
<syntaxhighlight lang="go">
// rectangle.go
type Rectangle struct {
    width int
    height int
}
 
func (rect Rectangle) Area() int {
    return rect.width * rect.height
}
</syntaxhighlight>


<syntaxhighlight lang="go">
<syntaxhighlight lang="go">
// declare an interface
// area_calculator.go
type Writer interface {
type AreaCalculator interface {
     Write([]byte) (int, error)
     Area() int
}
}
</syntaxhighlight>
</syntaxhighlight>


<syntaxhighlight lang="go">
<syntaxhighlight lang="go">
// implement an interface (automatic if methods match)
// main.go
type PrinterWriter struct {}
func LargestArea(a, b AreaCalculator) AreaCalculator {
    if a.Area() > b.Area() {
        return a
    }
    return b
}


func (w PrinterWriter) Write(data []byte) (int, error) {
func main() {
     // ...
    rect1 := Rectangle{5, 5}
    rect2 := Rectangle{2, 10}
     fmt.Println(LargestArea(rect1, rect2) == rect1)
}
}
</syntaxhighlight>
</syntaxhighlight>
</blockquote><!-- Basics -->
= Interfaces with Pointer Methods =
<blockquote>
When a method's <code>self</code> is a pointer, you must pass in a reference (<code>&value</code>) to the object in the method call.<br>
Reusing the last example


<syntaxhighlight lang="go">
<syntaxhighlight lang="go">
var w Writer = PrinterWriter{}  // <-- type 'Writer'
// rectangle.go
w.Write([]byte("Foobar"))
type Rectangle struct {
    width int
    height int
}
 
func (rect *Rectangle) Area() int { // <-- SEEME 'rect *Rectangle' indicates this method is added to a reference to this object
    return rect.width * rect.height
}
 
</syntaxhighlight>
 
<syntaxhighlight lang="go">
// area_calculator.go -- IDENTICAL
type AreaCalculator interface {
    Area() int
}
</syntaxhighlight>
</syntaxhighlight>


Libraries do not need to expose interfaces in go, you can create them for the subset of methods that are useful to you.
<syntaxhighlight lang="go">
</blockquote><!-- Basics -->
// main.go
func LargestArea(a, b AreaCalculator) AreaCalculator {
    if a.Area() > b.Area() {
        return a
    }
    return b
}
 
func main() {
    rect1 := Rectangle{5, 5}
    rect2 := Rectangle{2, 10}
    fmt.Println(rect1.Area())
    fmt.Println(rect2.Area())
    fmt.Println(LargestArea(&rect1, &rect2))  // <--- SEEME passing in reference!!
}
 
</syntaxhighlight>
</blockquote><!-- Interfaces with Pointer Methods -->
 
= Embedding in Interfaces =
<blockquote>
Embedding an interface within an interfaces includes it's method-signatures in the parent.
 
<syntaxhighlight lang="go">
type Walker interface {
    Walk nil
}
 
type BubblegumChewer interface {
    ChewGum nil
}
 
// includes all methods from previous two interfaces
type BubblegumChewingWalker interface {
    Walker
    BubblegumChewer
}
</syntaxhighlight>
</blockquote><!-- Embedding in Interfaces -->


== Primitive Interfaces ==
= Embedding in Structs (inherit methods) =
<blockquote>
<blockquote>
{{ TODO |
By embedding interfaces in a struct, you inherit the methods from the wrapped object.
test this code! }}
Interfaces can also be created on primitives.


<syntaxhighlight lang="go">
<syntaxhighlight lang="go">
type Doubler interface {
package main
    Double() int
 
import "fmt"
 
// interface
type Greeter interface {
Greet() string
}
 
// English/French implementations of Greeter
type EnglishGreeter struct{}
func (this *EnglishGreeter) Greet() string {
return "hello"
}
 
type FrenchGreeter struct{}
func (this *FrenchGreeter) Greet() string {
return "bonjours"
}
}


func (value *int) {
// Inherits Greet(), but can define it's own fields/methods
    *value = *value * 2
type GreeterWrapper struct {
Greeter
}
func (this *GreeterWrapper) GreetByName(name string) string {
return fmt.Sprintf("%s, %s", this.Greet(), name)
}
}


num := 10
func main() {
var doubler Doubler = &num
english := EnglishGreeter{}
num.Double() // 20
englishWrapper := GreeterWrapper{Greeter: &english}
fmt.Println(englishWrapper.Greet())
fmt.Println(englishWrapper.GreetByName("will"))
 
french := FrenchGreeter{}
frenchWrapper := GreeterWrapper{Greeter: &french}
fmt.Println(frenchWrapper.Greet())
fmt.Println(frenchWrapper.GreetByName("will"))
 
}
 
// hello
// hello, will
// bonjours
// bonjours, will
</syntaxhighlight>
</blockquote><!-- Embedding in Structs -->
 
= Empty Interfaces =
<blockquote>
Empty interfaces will accept any type within go.<br>
It is used for example by <code>fmt.Println()</code> so that it can accept any type of parameter.
 
<syntaxhighlight lang="go">
func typeInfo(obj interface{}) string {
    fmt.Printf("(%v, %T)\n", obj, obj)
}
</syntaxhighlight>
</blockquote><!-- Empty Interfaces -->
 
= Cast as Concretion =
<blockquote>
See [[golang variables]] for more details
 
<syntaxhighlight lang="go">
var foo MyInterface
foo = MyConcretion{}
castFoo := foo.(MyConcretion)
</syntaxhighlight>
</syntaxhighlight>
</blockquote><!-- Primitive Interfaces -->
</blockquote><!-- Cast as Concretion -->

Latest revision as of 00:07, 31 July 2022

Similar to other languages, interfaces in go define a contract of method-signatures that implementors must have.
Unlike other languages, golang interfaces are implicit -- an object with all of the required methods automatically satisfies an interface.
In go, it is encouraged to keep very small interfaces (ex. 1 method). The larger the interface, the weaker the abstraction.

Similar to other languages, a param can be typed to accept an interface, which abstracts the actual type that is received.

NOTE:

go interfaces define a contract of methods, but not struct fields.
you'll need to define getters/setters for fields

Basics

Libraries do not need to expose interfaces in go, you can create them for the subset of methods that are useful to you.

  • An interface is a promise that an object implements a set of method-signatures.
  • objects are automatically a part of an interface if they have methods, there is no implements Foo
  • Empty interfaces match all oject types in go. (ex fmt.Println() can accept any type)
// rectangle.go
type Rectangle struct {
    width int
    height int
}

func (rect Rectangle) Area() int {
    return rect.width * rect.height
}
// area_calculator.go
type AreaCalculator interface {
    Area() int
}
// main.go
func LargestArea(a, b AreaCalculator) AreaCalculator {
    if a.Area() > b.Area() {
        return a
    }
    return b
}

func main() {
    rect1 := Rectangle{5, 5}
    rect2 := Rectangle{2, 10}
    fmt.Println(LargestArea(rect1, rect2) == rect1)
}

Interfaces with Pointer Methods

When a method's self is a pointer, you must pass in a reference (&value) to the object in the method call.
Reusing the last example

// rectangle.go
type Rectangle struct {
    width int
    height int
}

func (rect *Rectangle) Area() int {  // <-- SEEME 'rect *Rectangle' indicates this method is added to a reference to this object
    return rect.width * rect.height
}
// area_calculator.go -- IDENTICAL
type AreaCalculator interface {
    Area() int
}
// main.go
func LargestArea(a, b AreaCalculator) AreaCalculator {
    if a.Area() > b.Area() {
        return a
    }
    return b
}

func main() {
    rect1 := Rectangle{5, 5}
    rect2 := Rectangle{2, 10}
    fmt.Println(rect1.Area())
    fmt.Println(rect2.Area())
    fmt.Println(LargestArea(&rect1, &rect2))  // <--- SEEME passing in reference!!
}

Embedding in Interfaces

Embedding an interface within an interfaces includes it's method-signatures in the parent.

type Walker interface {
    Walk nil
}

type BubblegumChewer interface {
    ChewGum nil
}

// includes all methods from previous two interfaces
type BubblegumChewingWalker interface {
    Walker
    BubblegumChewer
}

Embedding in Structs (inherit methods)

By embedding interfaces in a struct, you inherit the methods from the wrapped object.

package main

import "fmt"

// interface
type Greeter interface {
	Greet() string
}

// English/French implementations of Greeter
type EnglishGreeter struct{}
func (this *EnglishGreeter) Greet() string {
	return "hello"
}

type FrenchGreeter struct{}
func (this *FrenchGreeter) Greet() string {
	return "bonjours"
}

// Inherits Greet(), but can define it's own fields/methods
type GreeterWrapper struct {
	Greeter
}
func (this *GreeterWrapper) GreetByName(name string) string {
	return fmt.Sprintf("%s, %s", this.Greet(), name)
}

func main() {
	english := EnglishGreeter{}
	englishWrapper := GreeterWrapper{Greeter: &english}
	fmt.Println(englishWrapper.Greet())
	fmt.Println(englishWrapper.GreetByName("will"))

	french := FrenchGreeter{}
	frenchWrapper := GreeterWrapper{Greeter: &french}
	fmt.Println(frenchWrapper.Greet())
	fmt.Println(frenchWrapper.GreetByName("will"))

}

// hello
// hello, will
// bonjours
// bonjours, will

Empty Interfaces

Empty interfaces will accept any type within go.
It is used for example by fmt.Println() so that it can accept any type of parameter.

func typeInfo(obj interface{}) string {
    fmt.Printf("(%v, %T)\n", obj, obj)
}

Cast as Concretion

See golang variables for more details

var foo MyInterface
foo = MyConcretion{}
castFoo := foo.(MyConcretion)