Go for Javaneros (Javaïstes?)

#go4java

Francesc Campoy

Gopher and Developer Advocate

Google

What is Go?

Go is an open-source programming language

2

Who uses Go?

Google:

Others:

go.dev/wiki/GoUsers

3

Who uses Go?

Google Trends for golang
4

Why Go?

5

Simplicity

Minimal design

6

Consistency

Orthogonal features

By Kenny Louie from Vancouver, Canada CC-BY-2.0, via Wikimedia Commons
7

Readability

“The ratio of time spent reading (code) versus writing is well over 10 to 1 ... (therefore) making it easy to read makes it easier to write.”
― Robert C. Martin

8

Safety

Type safety, no buffer overflows, no pointer arithmetic.

9

Built-in concurrency features

“In a concurrent world, imperative is the wrong default!” - Tim Sweeney

Communicating Sequential Processes - Hoare (1978)

10

Speed

11

Let's dive in

12

Go and Java common aspects

Go and Java are

13

Object oriented flavors

Go is Object Oriented, but doesn't have the keywords:

14

All types are created equal

15

Go types

int, uint, int8, uint8, ...
bool, string
float32, float64
complex64, complex128
struct {
    Name string
    Age  int
}
[]int, [3]string, []struct{ Name string }
map[string]int
16

Kinds of types (continued)

*int, *Person
func(int, int) int
chan bool
interface {
    Start()
    Stop()
}
17

Type declarations

type [name] [specification]

Person is a struct type.

type Person struct {
    name string
    age  int
}

Celsius is a float64 type.

type Celsius float64
18

Function declarations

func [name] ([params]) [return value]
func [name] ([params]) ([return values])

A sum function:

func sum(a int, b int) int {
    return a + b
}

A function with multiple returned values:

func div(a, b int) (int, int)
    return a / b, a % b
}

Made clearer by naming the return values:

func div(den, div int) (q, rem int)
    return a / b, a % b
}
19

Method declarations

func ([receiver]) [name] ([params]) ([return values])

A method on a struct:

func (p Person) Major() bool {
    return p.age >= 18
}

But also a method on a float64:

func (c Celsius) Freezing() bool {
    return c <= 0
}

Constraint: Methods can be defined only on types declared in the same package.

// This won't compile
func (s string) Length() int { return len(s) }
20

Wait, pointers?

Use & to obtain the address of a variable.

a := "hello"
p := &a

Use * to dereference the pointer.

fmt.Print(*p + ", world")

No pointer arithmetic, no pointers to unsafe memory.

a := "hello"
p := &a

p += 4  // no, you can't
21

Why pointers?

Control what you pass to functions.

func double(x int) {
    x *= 2
}
func double(x *int) {
    *x *= 2
}

Control your memory layout.

22

Method declarations on pointers

Receivers behave like any other argument.

Pointers allow modifying the pointed receiver:

func (p *Person) IncAge() {
    p.age++
}

The method receiver is a copy of a pointer (pointing to the same address).

Method calls on nil receivers are perfectly valid (and useful!).

func (p *Person) Name() string {
    if p == nil {
        return "anonymous"
    }
    return p.name
}
23

Interfaces

24

Interfaces

An interface is a set of methods.

In Java:

interface Switch {
    void open();
    void close();
}

In Go:

type OpenCloser interface {
    Open()
    Close()
}
25

It's all about satisfaction

Java interfaces are satisfied explicitly.

Go interfaces are satisfied implicitly.

Picture by Gorupdebesanez CC-BY-SA-3.0, via Wikimedia Commons
26

Go: implicit satisfaction

If a type defines all the methods of an interface, the type satisfies that interface.

Benefits:

27

Structural subtyping

Think static duck typing, verified at compile time.

28

FuncDraw: an example on interfaces

29

FuncDraw: package parser

Package parse provides a parser of strings into functions.

func Parse(text string) (*Func, error) { ... }

Func is a struct type, with an Eval method.

type Func struct { ... }

func (p *Func) Eval(x float64) float64 { ... }
30

FuncDraw: package draw

Package draw generates images given a function.

func Draw(f *parser.Func) image.Image {
    for x := start; x < end; x += inc {
        y := f.Eval(x)
        ...
    }
}

draw depends on parser

Let's use an interface instead

type Evaluable interface {
    Eval(float64) float64
}

func Draw(f Evaluable) image.Image { ... }
31

Inheritance vs composition

32

Inheritance vs composition

Lots of articles have been written about the topic.

In general, composition is preferred to inheritance.

Lets see why.

33

Runner

    class Runner {
        private String name;

        public Runner(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public void run(Task task) {
            task.run();
        }

        public void runAll(Task[] tasks) {
            for (Task task : tasks) {
                run(task);
            }
        }
    }
34

RunCounter is-a Runner that counts

    class RunCounter extends Runner {
        private int count;

        public RunCounter(String message) {
            super(message);
            this.count = 0;
        }

        @Override public void run(Task task) {
            count++;
            super.run(task);
        }

        @Override public void runAll(Task[] tasks) {
            count += tasks.length;
            super.runAll(tasks);
        }

        public int getCount() {
            return count;
        }
    }
35

Let's run and count

What will this code print?

        RunCounter runner = new RunCounter("my runner");

        Task[] tasks = { new Task("one"), new Task("two"), new Task("three")};
        runner.runAll(tasks);

        System.out.printf("%s ran %d tasks\n", runner.getName(), runner.getCount());

Of course, this prints:

running one
running two
running three
my runner ran 6 tasks

Wait! How many?

36

My runner ran 6 tasks? Six?

Inheritance causes:

37

Solution: use composition

    class RunCounter {
        private Runner runner;
        private int count;

        public RunCounter(String message) {
            this.runner = new Runner(message);
            this.count = 0;
        }

        public void run(Task task) {
            count++;
            runner.run(task);
        }

        public void runAll(Task[] tasks) {
            count += tasks.length;
            runner.runAll(tasks);
        }

        // continued on next slide ...
38

Solution: use composition (continued)

        public int getCount() {
            return count;
        }

        public String getName() {
            return runner.getName();
        }
    }
39

Solution: use composition (continued)

Pros

Cons

public String getName() { return runner.getName(); }
40

There's no inheritance in Go

41

There's no inheritance in Go

Let's use composition directly:

type Runner struct{ name string }

func (r *Runner) Name() string { return r.name }

func (r *Runner) Run(t Task) {
    t.Run()
}

func (r *Runner) RunAll(ts []Task) {
    for _, t := range ts {
        r.Run(t)
    }
}

All very similar to the Java version.

42

RunCounter

RunCounter has a Runner field.

type RunCounter struct {
    runner Runner
    count  int
}

func NewRunCounter(name string) *RunCounter {
    return &RunCounter{runner: Runner{name}}
}

func (r *RunCounter) Run(t Task) {
    r.count++
    r.runner.Run(t)
}

func (r *RunCounter) RunAll(ts []Task) {
    r.count += len(ts)
    r.runner.RunAll(ts)
}

func (r *RunCounter) Count() int { return r.count }

func (r *RunCounter) Name() string { return r.runner.Name() }
43

Composition in Go

Same pros and cons as the composition version in Java.

We also have the boilerplate to proxy methods from Runner.

func (r *RunCounter) Name() string { return r.runner.Name() }

But we can remove it!

44

Struct embedding

Expressed in Go as unnamed fields in a struct.

It is still composition.

The fields and methods of the embedded type are defined on the embedding type.

Similar to inheritance, but the embedded type doesn't know it's embedded.

45

Example of struct embedding

Given a type Person:

type Person struct{ Name string }

func (p Person) Introduce() { fmt.Println("Hi, I'm", p.Name) }

We can define a type Employee embedding Person:

type Employee struct {
    Person
    EmployeeID int
}

All fields and methods from Person are available on Employee:

    var e Employee
    e.Name = "Peter"
    e.EmployeeID = 1234

    e.Introduce()
46

Struct embedding

type RunCounter2 struct {
    Runner
    count  int
}

func NewRunCounter2(name string) *RunCounter2 {
    return &RunCounter2{Runner{name}, 0}
}

func (r *RunCounter2) Run(t Task) {
    r.count++
    r.Runner.Run(t)
}

func (r *RunCounter2) RunAll(ts []Task) {
    r.count += len(ts)
    r.Runner.RunAll(ts)
}

func (r *RunCounter2) Count() int { return r.count }
47

Is struct embedding like inheritance?

No, it is better!

It is composition.

It is more general.

48

Is struct embedding like inheritance?

Struct embedding is selective.

// WriteCounter tracks the total number of bytes written.
type WriteCounter struct {
    io.ReadWriter
    count int
}

func (w *WriteCounter) Write(b []byte) (int, error) {
    w.count += len(b)
    return w.ReadWriter.Write(b)
}

WriteCounter can be used with any io.ReadWriter.

// +build ignore,OMIT

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
)

var (
	_ = bytes.Buffer{}
	_ = os.Stdout
)

// WriteCounter tracks the total number of bytes written.
type WriteCounter struct {
	io.ReadWriter
	count int
}

func (w *WriteCounter) Write(b []byte) (int, error) {
	w.count += len(b)
	return w.ReadWriter.Write(b)
}

// MAIN OMIT
func main() {
    buf := &bytes.Buffer{}
    w := &WriteCounter{ReadWriter: buf}

    fmt.Fprintf(w, "Hello, gophers!\n")
    fmt.Printf("Printed %v bytes", w.count)
}
49

Easy mocking

What if we wanted to fake a part of a net.Conn?

type Conn interface {
        Read(b []byte) (n int, err error)
        Write(b []byte) (n int, err error)
        Close() error
        LocalAddr() Addr
        RemoteAddr() Addr
        SetDeadline(t time.Time) error
        SetReadDeadline(t time.Time) error
        SetWriteDeadline(t time.Time) error
}

I want to test handleCon:

func handleConn(conn net.Conn) {
50

Struct embedding of interfaces

WARNING : Cool stuff

If a type T has an embedded field of a type E, all the methods of E will be defined on T.

Therefore, if E is an interface T satisfies E.

51

Struct embedding of interfaces (continued)

We can test handleCon with the loopBack type.

type loopBack struct {
    net.Conn
    buf bytes.Buffer
}

Any calls to the methods of net.Conn will fail, since the field is nil.

We redefine the operations we support:

func (c *loopBack) Read(b []byte) (int, error) {
    return c.buf.Read(b)
}

func (c *loopBack) Write(b []byte) (int, error) {
    return c.buf.Write(b)
}
52

Concurrency

53

Concurrency

It is part of the language, not a library.

Based on two concepts:

So cheap you can use them whenever you want.

54

Sleep and talk

func sleepAndTalk(t time.Duration, msg string) {
    time.Sleep(t)
    fmt.Printf("%v ", msg)
}

We want a message per second.

// +build ignore,OMIT

package main

import (
	"fmt"
	"time"
)

func sleepAndTalk(t time.Duration, msg string) {
	time.Sleep(t)
	fmt.Printf("%v ", msg)
}

func main() {
    sleepAndTalk(0*time.Second, "Hello")
    sleepAndTalk(1*time.Second, "Gophers!")
    sleepAndTalk(2*time.Second, "What's")
    sleepAndTalk(3*time.Second, "up?")
}

What if we started all the sleepAndTalk concurrently?

Just add go!

55

Concurrent sleep and talk

// +build ignore,OMIT

package main

import (
	"fmt"
	"time"
)

func sleepAndTalk(t time.Duration, msg string) {
	time.Sleep(t)
	fmt.Printf("%v ", msg)
}

func main() {
    go sleepAndTalk(0*time.Second, "Hello")
    go sleepAndTalk(1*time.Second, "Gophers!")
    go sleepAndTalk(2*time.Second, "What's")
    go sleepAndTalk(3*time.Second, "up?")
}

That was fast ...

When the main goroutine ends, the program ends.

56

Concurrent sleep and talk with more sleeping

// +build ignore,OMIT

package main

import (
	"fmt"
	"time"
)

func sleepAndTalk(t time.Duration, msg string) {
	time.Sleep(t)
	fmt.Printf("%v ", msg)
}

func main() {
    go sleepAndTalk(0*time.Second, "Hello")
    go sleepAndTalk(1*time.Second, "Gophers!")
    go sleepAndTalk(2*time.Second, "What's")
    go sleepAndTalk(3*time.Second, "up?")
    time.Sleep(4 * time.Second)
}

But synchronizing with Sleep is a bad idea.

57

Communicating through channels

sleepAndTalk sends the string into the channel instead of printing it.

func sleepAndTalk(secs time.Duration, msg string, c chan string) {
    time.Sleep(secs * time.Second)
    c <- msg
}

We create the channel and pass it to sleepAndTalk, then wait for the values to be sent.

// +build ignore,OMIT

package main

import (
	"fmt"
	"time"
)

func sleepAndTalk(secs time.Duration, msg string, c chan string) {
	time.Sleep(secs * time.Second)
	c <- msg
}

func main() {
    c := make(chan string)

    go sleepAndTalk(0, "Hello", c)
    go sleepAndTalk(1, "Gophers!", c)
    go sleepAndTalk(2, "What's", c)
    go sleepAndTalk(3, "up?", c)

    for i := 0; i < 4; i++ {
        fmt.Printf("%v ", <-c)
    }
}
58

Let's count on the web

We receive the next id from a channel.

var nextID = make(chan int)

func handler(w http.ResponseWriter, q *http.Request) {
    fmt.Fprintf(w, "<h1>You got %v<h1>", <-nextID)
}

We need a goroutine sending ids into the channel.

// +build ignore,OMIT

package main

import (
	"fmt"
	"net/http"
)

var nextID = make(chan int)

func handler(w http.ResponseWriter, q *http.Request) {
	fmt.Fprintf(w, "<h1>You got %v<h1>", <-nextID)
}

func main() {
    http.HandleFunc("/next", handler)
    go func() {
        for i := 0; ; i++ {
            nextID <- i
        }
    }()
    http.ListenAndServe("localhost:8080", nil)
}

localhost:8080/next

59

Let's fight!

select allows us to chose among multiple channel operations.

// +build ignore,OMIT

package main

import (
	"fmt"
	"net/http"
)

var battle = make(chan string)

func handler(w http.ResponseWriter, q *http.Request) {
    select {
    case battle <- q.FormValue("usr"):
        fmt.Fprintf(w, "You won!")
    case won := <-battle:
        fmt.Fprintf(w, "You lost, %v is better than you", won)
    }
}

func main() {
	http.HandleFunc("/fight", handler)
	http.ListenAndServe("localhost:8080", nil)
}

Go - localhost:8080/fight?usr=go
Java - localhost:8080/fight?usr=java

60

Chain of gophers

Ok, I'm just bragging here

61

Chain of gophers

// +build ignore,OMIT

package main

import (
	"fmt"
	"time"
)

func f(left, right chan int) {
    left <- 1 + <-right
}

func main() {
    start := time.Now()
    const n = 1000
    leftmost := make(chan int)

    right := leftmost
    left := leftmost
    for i := 0; i < n; i++ {
        right = make(chan int)
        go f(left, right)
        left = right
    }

    go func(c chan int) { c <- 0 }(right)

    fmt.Println(<-leftmost, time.Since(start))
}
62

Concurrency is very powerful

And there's lots to learn!

63

In conclusion

Go is simple, consistent, readable, and fun.

All types are equal

Implicit interfaces

Use composition instead of inheritance

Concurrency is awesome, and you should check it out.

64

What to do next?

Learn Go on your browser with go.dev/tour

Find more about Go on go.dev

Join the community at golang-nuts

Link to the slides /talks/2014/go4java.slide

65

Thank you

Francesc Campoy

Gopher and Developer Advocate

Google

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)