Skip to content
JavaGo
Part II · Types & Data

Types & Variables

Zero values instead of null, explicit integer sizes, no implicit conversions, and enums built from named types and iota.

Section 03 of 31 7 min read

Go is statically typed like Java, but stricter in some places and simpler in others. Every variable has a usable zero value, integer sizes are explicit, and there are no implicit numeric conversions at all.

Declaring variables

Java — declarations
int x = 10;
int y;          // field: 0; local: must assign first
String s;       // field: null (!)
var z = 10;     // Java 10+: local type inference
final int MAX = 3;
Go — var and :=
var x int = 10 // explicit type and value
var y int      // zero value: 0
var s string   // zero value: "" (never nil)
z := 10        // short form: inferred, functions only
const Max = 3  // compile-time constant

var (          // grouped declarations
    host = "localhost"
    port = 8080
)

Java: a field before assignment

Go: the zero value of each type

Fig. 3.1What a field holds before you assign it. A Java object field starts as null. Every Go type has a ready-to-use zero value, and only pointers, slices, maps, channels, functions and interfaces can be nil.

Integer types: size is explicit

pick integer types intentionally
// Java: byte(8) short(16) int(32) long(64), all signed, plus char(16, unsigned)
// Go: signed and unsigned, named by bit width

var a int    // 64-bit on 64-bit platforms. The default choice.
var b int32  // ±2.1 billion. Alias: rune (a Unicode code point)
var c int64  // ±9.2 quintillion. IDs, Unix timestamps
var d uint8  // 0–255. Alias: byte (raw binary data)
var e uint64 // bit masks, hashes, IDs from external systems

// Quick guide:
//   int     counters, indexes, lengths (len() returns int)
//   int64   database keys, time.Duration, anything on the wire
//   byte    binary data: []byte is the standard buffer type
//   rune    one Unicode code point (see the Strings section)
//   uint*   bit manipulation and wire formats, not "can't be negative"

No implicit conversions

Java — widening happens silently
int count = 3;
long total = count;        // implicit widening
double avg = total / 2.0;  // implicit promotion

Integer boxed = count;     // autoboxing
String s = "n=" + count;   // implicit toString
Go — every conversion is written out
count := 3
var total int64 = int64(count) // must convert
avg := float64(total) / 2.0    // must convert

// There is no boxing: int is always a value.
s := "n=" + strconv.Itoa(count) // no implicit toString
s2 := fmt.Sprintf("n=%d", count)

// var t int64 = count  // compile error: int ≠ int64

Named types and enums with iota

Java — enum
public enum Status {
    IDLE, ACTIVE, DRAINING, STOPPED;
}

Status s = Status.ACTIVE;
switch (s) {
    case ACTIVE -> start();
    default -> {}
}
Go — named type + iota constants
// A distinct type, not an alias for int
type Status int

const (
    StatusIdle     Status = iota // 0
    StatusActive                 // 1
    StatusDraining               // 2
    StatusStopped                // 3
)

s := StatusActive
switch s {
case StatusActive:
    start()
}
what a named type does and does not protect you from
func SetStatus(s Status) {}

SetStatus(StatusActive) // ok
SetStatus(2)            // ok: untyped constant converts implicitly

n := 2
SetStatus(n)            // compile error: int is not Status
SetStatus(Status(n))    // ok: explicit conversion, even if n is 42
giving an enum a String method
// Java enums have name() for free. In Go, implement fmt.Stringer:
func (s Status) String() string {
    switch s {
    case StatusIdle:
        return "idle"
    case StatusActive:
        return "active"
    case StatusDraining:
        return "draining"
    case StatusStopped:
        return "stopped"
    }
    return fmt.Sprintf("Status(%d)", int(s))
}

fmt.Println(StatusDraining) // "draining": fmt calls String()

// For long enums, generate it instead:
//go:generate go tool stringer -type=Status

Defined types vs type aliases

type T U vs type T = U
type UserID int64   // defined type: new, distinct type
type Celsius float64 // can have its own methods

type Bytes = []byte  // alias: just another name, same type
                     // cannot add methods to it

var id UserID = 42
var n int64 = id         // compile error: UserID is not int64
var m int64 = int64(id)  // ok

var b Bytes = []byte("hi") // ok: identical types
↑ ↓ to navigateEnter to openEsc to close