Golang basics…...for Java developers
Robert Stern
Agenda
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Java vs. Go
All you need is…
https://play.golang.org/
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
History and features
Created in 2007 at Google, announced in 2009, v1.0 released in 2012) with strong dedication for server side programming.
Compiled, statically typed, garbage collected language with multiple architecture/OS support (i386, amd64, arm/Windows, Linux, *BSD, etc..)
(very) fast compilation AND execution time with low resource consumption
Powerful cli (go get, go build, go test, go install…)
Current stable version is 1.6.3 and still being actively developed
What is missing or disturbing?
No generics
Not an OO language
No constructors
No operator/method overloading
No circular package imports
No mandatory semicolons
GC pause is unpredictable
What is non-java-isch?
Multiple return values + the blank variable
Golang is a pass-by-value language
Lightweight concurrency with goroutines and channels
No SE/EE separation. All server stuff is built-in.
No exceptions, use error instead
Compile time error about unused imports and variables
No maven/ant like build system. Uses Makefile+vendoring+go get
Famous projects, companies
Projects:
App Engine with Go, AWS with Go SDK, Docker, Kubernetes, Grafana, Terraform, Gogs (Go Git Service), etcd, drone.io, Syncthing, CockroachDB, rkt, nsq, consul, boltDB, weave, swarm, vulcand, gryffin, fleet, minio…
Companies:
Google (of course…), Netflix, Dropbox, CloudFlare, SoundCloud, BBC, Uber, Couchbase, MongoDB...
Google trends - golang
A bit bigger picture about the trends
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
Hello world in golang:
package main
// imports
import "fmt"
/*
This is the main function
*/
func main() {
fmt.Println("Hello, World!")
}
Try it in the Go playground!
Hello world
Go project structure
❏ Workspace ($GOPATH or %GOPATH%)
❏ src/pkg/bin roots
❏ Repository (github.com, etc…)
❏ Package
❏ Go source files
Structure example
PackagesLike in Java, golang also uses packages to organize code, but in a bit different format.
Keyword: package <name>
The app starts in the main package.
the path to the package’s directory is the import path
The package name is the last element of the import path:
github.com/zooplus/golang-app/stringutil → stringutil
Accessing stringutil functions/elements: stringutil.<name>
import ("reflect""strings""github.com/emicklei/go-restful"
)
import "reflect"import "strings"import "github.com/emicklei/go-restful"
Importing packages
Package typesTo use one or more functions in a package we need to import it into our code. We can import system, local and remote packages.
System packages are the golang out of box packages like http, testing, etc..
Local package resides in your repository, no full path needed
Remote packages has complete import path pointing to the server like: github.com/emicklei/go-restful
Getting the dependenciesFor remote packages use go get to fetch into your workspace. We have two syntaxes
go get in the repository folder downloads all remote packages based on the imports
go get <remote path> downloads the specified package for later use
Variables, constantspackage main
import "fmt"
const greeting = "Hello"
var name string
func main() {name = "Zooplus"space := " " fmt.Println(greeting + space + name)
}
if _, err := os.Stat(path); os.IsNotExist(err) {fmt.Printf("%s does not exist\n", path)
}
The blank identifier“The blank identifier can be assigned or declared with any value of any type, with the value discarded harmlessly”
(https://golang.org/doc/effective_go.html#blank)
Exported names“In Go, a name is exported if it begins with a capital letter.
For example, Pizza is an exported name, as is Pi, which is exported from the math package.”
package main
import ("fmt""math"
)
func main() {fmt.Println(math.pi)
}
Practice!Refactor the greeting app to use fmt.Printf(format, args...)** and display the following message:
Hello Zooplus, today is Friday.
The name of the day as string can be printed with:
import "time"
...time.Now().Format("Monday")
**(Use %s for string)
A solutionpackage main
import ("fmt""time"
)
func main() {name := "Zooplus"fmt.Printf("Hello %s, today is %s.", name, time.Now().Format("Monday"))
}
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
Basic typesbool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
complex64 complex128 - “math/cmplx”:
var number complex128 = cmplx.Sqrt(-5 + 12i)
Pointers - bit.ly/2a58jnIpackage main
import "fmt"
func main() {i, j := 42, 2701
p := &i // point to ifmt.Println(*p) // read i through the pointer*p = 21 // set i through the pointerfmt.Println(i) // see the new value of i
p = &j // point to j*p = *p / 37 // divide j through the pointerfmt.Println(j) // see the new value of j
}
package main
var (a intb int32c int16
)
func main() {a = 1b = ac = a
}
No autoboxing! You should convert your types by yourself.
package main
import "fmt"
var (a intb int32c int16
)
func main() {a = 1b = int32(a)c = int16(a)fmt.Printf("a: %d, b: %d, c: %d", a, b, c)
}
Type conversion
prog.go:11: cannot use a (type int) as type int32 in assignmentprog.go:12: cannot use a (type int) as type int16 in assignmenta: 1, b: 1, c: 1
Structtype ApacheLogRecord struct {
ip string
time string
method, uri, protocol string
status intresponseBytes intreferer
stringuserAgent
string}
1. result = &ApacheLogRecord{ip:
nvl(req.RemoteAddr),time: timeFormatted,method:
nvl(req.Method),uri:
nvl(req.URL.Path),protocol: nvl(req.Proto),status:
resp.StatusCode(),responseBytes:
resp.ContentLength(),referer:
nvl(req.Referer()),userAgent:
nvl(req.UserAgent()),}2.) result = new(ApacheLogRecord)
package main
import "fmt"
type Animal struct { legCount int }// define a behavior for Animalfunc (a Animal) numberOfLegs() int { return a.legCount}
type Dog struct { Animal //anonymous field Animal}
func main() { d := Dog{Animal{4}} //no method defined for Dog, but we have the same behavior as Animal. fmt.Println("A Dog has this many legs: ", d.numberOfLegs()) }
Embedding, inheritance -bit.ly/2axY1yu
Receiver methods -bit.ly/2a56VkSpackage mainimport "fmt"type Animal struct {
legCount int}func (a Animal) numberOfLegs() int {
return a.legCount}func (a Animal) setLegs(c int) {
a.legCount = c}type Mammal struct {
weight int}func (m Mammal) getWeight() int {
return m.weight}func (m Mammal) setWeight(w int) {
m.weight = w}type Dog struct {
AnimalMammal
}func main() {
d := Dog{Animal{4}, Mammal{10}}fmt.Printf("A Dog has this many legs: %d oh... and weight: %d", d.numberOfLegs(), d.getWeight())
}
Pointer receivers -bit.ly/2aKK7sipackage mainimport "fmt"type Animal struct {
legCount int}func (a Animal) numberOfLegs() int {
return a.legCount}func (a Animal) setLegs(c int) {
a.legCount = c}func (a *Animal) setLegsPtr(c int) {
a.legCount = c}type Dog struct {
Animal //anonymous field Animal}func main() {
d := Dog{Animal{4}}fmt.Println("A Dog has this many legs: ", d.numberOfLegs())d.setLegs(5)fmt.Println("A Dog has this many legs: ", d.numberOfLegs())d.setLegsPtr(6)fmt.Println("A Dog has this many legs: ", d.numberOfLegs())
}
Arrays -bit.ly/2ahx5EAArrays has fix size and type with zero based indexing:
var integers [20]int
package main
import "fmt"
func main() {var a [2]string // std declaration a[0] = "Hello"a[1] = "Zooplus"fmt.Println(a[0], a[1])fmt.Println(a)fibonacci := [6]int{1, 1, 2, 3, 5, 7} // on-the-fly declarationfmt.Println(fibonacci)
}
Slices - bit.ly/2ahVarPSlice is a dynamic version of array (a sort of):
var integers []int // decleration of a slice without size
package main
import "fmt"
func main() {fibonacci := [6]int{1, 1, 2, 3, 5, 7}var p []int = fibonacci[1:4]fmt.Println(p)
}
Slices are not storing data - bit.ly/2aai1Wxpackage main
import "fmt"
func main() {names := [4]string{
"John","Paul","George","Ringo",
}fmt.Println(names)
a := names[0:2]b := names[1:3]fmt.Println(a, b)
b[0] = "XXX"fmt.Println(a, b)fmt.Println(names)
}
Slices - boundsThere are default low and high bounds for slices. Given the following array:
var a [10]int
The following slice declarations are equivalent:
a[0:10]
a[:10]
a[0:]
a[:]
Creating slice:
a := make([]int, 5) // len(a)=5
Creating slice with capacity:
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
Appending slice:
b = append(b, 10)
Further reading: https://blog.golang.org/go-slices-usage-and-internals
Slices - create, append
Arrays, slices - for loop as foreachIterate through an array/slice
for index, value := range arr { fmt.Println("Index:", index, "Value:", value)}Get the index only:
for index := range arr { fmt.Println("Index:", index)}
Get the values:
for _, value := range arr { fmt.Println("Value:", value)}
Maps - declaration, initializationMaps are not initialized by default, we should use make:
package main
import "fmt"
var m map[string]string
func main() {m = make(map[string]string)m["Zooplus"] = "Gut"m["Bitiba"] = "Gut"m["Fressnapf"] = "Falsch"fmt.Println(m["Zooplus"])
}
Maps - Mutating mapsm[key] = item // upsert
item = m[key] // retrieve
delete (m, key) // guess….
_, ok = m[key] // ok == true if key exists, map can store/return nil
Maps are not thread safe!!!
Maps - for loop as foreachIterate through a map:
for key, value := range m { fmt.Println("Key:", key, "Value:", value)}Get the keys:
for key := range m { fmt.Println("Key:", key)}
Get the values:
for _, value := range m { fmt.Println("Value:", value)}
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
The one and only: for loop// normal loopsum := 0// init-condition-postfor i := 0; i < 10; i++ {
sum += i}fmt.Println(sum)
// partial loopsum := 0// init and post are optionalfor ; sum < 10; {
sum += sum}
// “while” loopsum := 1// init and post are optionalfor sum < 1000 {
sum += sum}fmt.Println(sum)
// infinite loopfor {
}
func GetProject(req *restful.Request, resp *restful.Response) {resp.Header().Set("Pragma", "no-cache")//pId := req.PathParameter("projectId")
if project, ok := projects.GetProjectByIdOrName(pId); ok {
ReturnJSON(resp, http.StatusOK, project)}ReturnMessage(resp, http.StatusNotFound, "Project not
found!")}
If-then-elseexistingPipeline, ok := nameSpace.Pipelines[newPipeline.Name]if ok {
log.Debug("Entry exists!")} else {
nameSpace.Pipelines[newPipeline.Name] = existingPipelinelog.Debug("New entry stored")
}
?
SwitchSwitch in Golang is a bit different from Java:
No automatic fallthrough, no need to put break at the end in every cases
You can delegate expression evaluation into the case block
fallthrough keyword for java-ish behavior
Multiple conditions in one case (no empty cases like in java)
Not limited to string and int
Type switch can be used for interface{} to find out the real type (instanceof)
Switch - no fallthrough by defaultswitch c {case '&': esc = "&"case '\'': esc = "'"case '<': esc = "<"case '>': esc = ">"case '"': esc = """default: panic("unrecognized escape character")}
Switch - strings are welcomeswitch syscall.OS {case "windows":...case "plan9":...default:...}
Switch - (multiple)expression(s) in casesswitch { case '0' <= c && c <= '9': return c - '0' case 'a' <= c && c <= 'f': return c - 'a' + 10 case 'A' <= c && c <= 'F': return c - 'A' + 10}
switch chars[code].category { case "de", "uk", "fr", "sp", "nl": return true }
Switch - Type switchswitch v.(type) { case int: return "int" case string: return "string" default: return "unknown" }
Switch - you can break itswitch argv[0] {case "echo": fmt.Print(argv[1:]...)case "cat": if len(argv) <= 1 { fmt.Println("Usage: cat <filename>") break } PrintFile(argv[1])default: fmt.Println("Unknown command; try 'echo' or 'cat'")}
Switch - fallthroughswitch len(src) {default: v |= uint32(src[3]) fallthroughcase 3: v |= uint32(src[2]) << 8 fallthroughcase 2: v |= uint32(src[1]) << 16 fallthroughcase 1: v |= uint32(src[0]) << 24}
Errors instead of exceptionsGolang has no exceptions
and of course there are no checked exceptions :)))))))
No try-catch-finally.
Error is just an interface
Functions are often returning a value and an error together.Checking this error for nil is the exception handling in Golang:
// validatepipeline, err := validateYaml(buf.Bytes())if err != nil {
errorMessage := fmt.Sprintf("Error parsing pipeline config: %s", err)log.Error(errorMessage)http.Error(w, errorMessage, http.StatusBadRequest)
} else {...}
NO CHECKED EXCEPTIONS!!!
Deferfunc CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close() dst, err := os.Create(dstName) if err != nil { return }
defer dst.Close() defer fmt.Println(“All closed”) return io.Copy(dst, src)}
Panic and Recover -bit.ly/2a5m0mJfunc main() {
f()fmt.Println("Returned normally from f.")
}func f() {
defer func() {if r := recover(); r != nil {
fmt.Println("Recovered in f", r)}
}()fmt.Println("Calling g.")g(0)fmt.Println("Returned normally from g.")
}func g(i int) {
if i > 3 {fmt.Println("Panicking!")panic(fmt.Sprintf("%v", i))
}defer fmt.Println("Defer in g", i)fmt.Println("Printing in g", i)g(i + 1)
}
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
Functions -bit.ly/2a5mg4Wpackage main
import "fmt"
func swap(x, y string) (string, string) { // also can use (x string, y string)return y, x
}
func main() {a, b := swap("hello", "zooplus")fmt.Println(a, b)
}
Functions -bit.ly/2aymMe6package main
import "fmt"
func split(sum int) (x, y int) {x = sum * 4 / 9y = sum - xreturn
}
func main() {fmt.Println(split(17))
}
InterfacesGolang has no “implements” keyword. If you implement all methods defined by the interface, then you are implemented the interface implicitly.
package mainimport "fmt"type I interface {
M()}type T struct {
S string}// This method means type T implements the interface I,// but we don't need to explicitly declare that it does so.
func (t T) M() {fmt.Println(t.S)
}func main() {
var i I = T{"hello"}i.M()
}
The empty interface - bit.ly/2awwq3ZLike Object in Java, Golang has the interface{} as type that compatible with every value (even with primitives).
So it can hold any value. You need type assertion to find out what is it:
package mainimport "fmt"func main() {
var i interface{} = "hello"s := i.(string)fmt.Println(s)s, ok := i.(string)fmt.Println(s, ok)f, ok := i.(float64)fmt.Println(f, ok)f = i.(float64) // panicfmt.Println(f)
}
Reflection“In computer science, reflection is the ability of a computer program to examine and modify its own structure and behavior (specifically the values, meta-data, properties and functions) at runtime.” /Wikipedia/
Golang has a reflection package to inspect structs, interfaces or methods runtime like in Java and also you can change the values of the fields.
What you’ll miss is some bytecode manipulation and proxy generation. Mockito like framework is not possible in Golang.
See more: http://blog.ralch.com/tutorial/golang-reflection/
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
Similarities with JavaGoroutine = Thread
Locks (R, RW)
Atomic package thread safe variable handling
Waitgroup = Join
Differences from JavaGoroutine is lightweight, but not reusable
No Threadlocal or Executorservice in Golang
Golang has no thread safe map implementation by default (https://github.com/streamrail/concurrent-map)
Golang is a pass-by-value language
Golang uses channels to communicate between threads
Golang unsafe package is more limited
To summarize: Java originally built-on the shared memory concept, but the Golang is focusing on the communication with channels
Channelsch := make(chan int) // single item channelv:= 5ch <- v // Send v to channel ch.v := <-ch // Receive from ch, and // assign value to v.
ch := make(chan int,100) // buffered channelv:= 5ch <- v // Send v to channel ch.v := <-ch // Receive from ch, and // assign value to v.
Looping on a channelpackage mainimport (
"fmt")func fibonacci(n int, c chan int) {
x, y := 0, 1for i := 0; i < n; i++ {
c <- xx, y = y, x+y
}
close(c)}func main() {
c := make(chan int, 10)go fibonacci(cap(c), c)for i := range c {
fmt.Println(i)}
}
Overview
Basic syntax and structure
Datatypes
Flow control
Functions, interfaces
Concurrency
Java vs. Go
Go and Java common pointsC like (braces)
Garbage collector
Memory safety (nil/null references, runtime bounds checks)
Statically typed
Variables initialized by default(zero/nil/false)
Methods, Interfaces
Type assertions (instanceof)
Reflection
Go and Java differencesNative code without VM + statically linked binaries
Control over memory layout
Function values and lexical closures
Built-in strings (UTF-8)
Built-in generic maps and arrays/slices
Built-in concurrency
Go missing parts (by intention…)No inheritance
No constructors
No classes
No implements
No final
No exceptions
No annotations
No generics
!!! Spoiler !!!
A restful API in GolangBuilt with Makefile
Dockerized
Documented with Swagger
Tested with Cucumber
Good to know...
Next stepsGo by example (https://gobyexample.com)
Tutorial (https://tour.golang.org/welcome/1)
Effective go (https://golang.org/doc/effective_go.html)
Awesome Go: https://go.libhunt.com/
Recommended frameworksGorilla: http://www.gorillatoolkit.org/ OR https://github.com/emicklei/go-restful (with Swagger)
BDD: https://github.com/DATA-DOG/godog
Configuration: https://github.com/spf13/viper
Logging: https://github.com/Sirupsen/logrus
CLI writing: https://github.com/spf13/cobra
Microservices: https://gokit.io/
Thank you!
Recommended