BAB 7: Data Types - Abd. Asis

BAB 7: Data Types

Data type adalah konsep fundamental dalam programming yang menentukan jenis data yang dapat disimpan dan dimanipulasi dalam program. Go memiliki sistem type yang kuat dan static, yang berarti setiap variable harus memiliki type yang sudah ditentukan saat compile time.

Kategori Data Types di Go

Go memiliki beberapa kategori data type:

  1. Basic Types: int, float, bool, string
  2. Composite Types: array, slice, map, struct, channel
  3. Reference Types: pointer, interface, function
  4. Special Types: interface{}, nil

Mari kita bahas satu per satu.

Basic Types

Integer Types

Go menyediakan berbagai ukuran integer untuk kebutuhan yang berbeda:

package main

import "fmt"

func main() {
    // Signed integers
    var a int8 = 127       // -128 to 127
    var b int16 = 32767    // -32,768 to 32,767
    var c int32 = 2147483647 // -2,147,483,648 to 2,147,483,647
    var d int64 = 9223372036854775807 // sangat besar
    
    // Unsigned integers
    var e uint8 = 255      // 0 to 255
    var f uint16 = 65535   // 0 to 65,535
    var g uint32 = 4294967295 // 0 to 4,294,967,295
    var h uint64 = 18446744073709551615 // sangat besar
    
    // Platform dependent
    var i int = 42         // int32 atau int64 tergantung platform
    var j uint = 42        // uint32 atau uint64 tergantung platform
    var k uintptr = 12345  // untuk menyimpan pointer address
    
    fmt.Println(a, b, c, d, e, f, g, h, i, j, k)
}

Floating Point Types

package main

import "fmt"

func main() {
    var f32 float32 = 3.14159  // 32-bit floating point
    var f64 float64 = 3.141592653589793 // 64-bit floating point
    
    // Default adalah float64
    var pi = 3.14159 // float64
    
    fmt.Printf("float32: %.5f\n", f32)
    fmt.Printf("float64: %.15f\n", f64)
    fmt.Printf("default: %.5f\n", pi)
}

Boolean Type

package main

import "fmt"

func main() {
    var isTrue bool = true
    var isFalse bool = false
    var defaultBool bool // false (zero value)
    
    fmt.Println("isTrue:", isTrue)
    fmt.Println("isFalse:", isFalse)
    fmt.Println("defaultBool:", defaultBool)
    
    // Boolean operations
    result := isTrue && !isFalse
    fmt.Println("result:", result) // true
}

String Type

package main

import "fmt"

func main() {
    var nama string = "Budi Santoso"
    var alamat = "Jakarta"
    kota := "Bandung"
    
    // String concatenation
    fullAddress := alamat + ", " + kota
    
    // String length
    panjang := len(nama)
    
    // Access character (returns byte)
    firstChar := nama[0]
    
    fmt.Println("Nama:", nama)
    fmt.Println("Alamat lengkap:", fullAddress)
    fmt.Println("Panjang nama:", panjang)
    fmt.Println("Karakter pertama:", firstChar)
    
    // Multiline string
    pesan := `Ini adalah string
yang bisa lebih dari
satu baris tanpa escape character`
    fmt.Println(pesan)
}

Composite Types

Array

Array adalah koleksi elemen dengan ukuran tetap dan type yang sama:

package main

import "fmt"

func main() {
    // Deklarasi array
    var numbers [5]int                    // [0 0 0 0 0]
    var names [3]string = [3]string{"Ali", "Budi", "Citra"}
    colors := [4]string{"merah", "hijau", "biru", "kuning"}
    
    // Array dengan ukuran otomatis
    fruits := [...]string{"apel", "jeruk", "mangga"}
    
    // Mengakses elemen array
    numbers[0] = 10
    numbers[1] = 20
    
    fmt.Println("Numbers:", numbers)
    fmt.Println("Names:", names)
    fmt.Println("Colors:", colors)
    fmt.Println("Fruits:", fruits)
    fmt.Println("Panjang fruits:", len(fruits))
    
    // Iterasi array
    for i, value := range colors {
        fmt.Printf("Index %d: %s\n", i, value)
    }
}

Slice

Slice adalah versi dinamis dari array:

package main

import "fmt"

func main() {
    // Membuat slice
    var numbers []int                    // slice kosong
    names := []string{"Ali", "Budi"}     // slice dengan data
    colors := make([]string, 3)         // slice dengan length 3
    
    // Menambah elemen ke slice
    numbers = append(numbers, 1, 2, 3)
    names = append(names, "Citra")
    
    // Slice dari array
    arr := [5]int{1, 2, 3, 4, 5}
    slice1 := arr[1:4]  // [2 3 4]
    slice2 := arr[:3]   // [1 2 3]
    slice3 := arr[2:]   // [3 4 5]
    
    fmt.Println("Numbers:", numbers)
    fmt.Println("Names:", names)
    fmt.Println("Colors:", colors)
    fmt.Println("Slice1:", slice1)
    fmt.Println("Slice2:", slice2)
    fmt.Println("Slice3:", slice3)
    
    // Length dan capacity
    fmt.Printf("Slice1 - len: %d, cap: %d\n", len(slice1), cap(slice1))
}

Map

Map adalah struktur data key-value:

package main

import "fmt"

func main() {
    // Membuat map
    var ages map[string]int = make(map[string]int)
    scores := map[string]int{
        "Alice": 95,
        "Bob":   87,
        "Charlie": 92,
    }
    
    // Menambah/mengubah data
    ages["Ali"] = 25
    ages["Budi"] = 30
    scores["Diana"] = 88
    
    // Mengakses data
    aliAge := ages["Ali"]
    bobScore, exists := scores["Bob"] // cek apakah key ada
    
    fmt.Println("Ages:", ages)
    fmt.Println("Scores:", scores)
    fmt.Println("Ali's age:", aliAge)
    fmt.Println("Bob's score:", bobScore, "exists:", exists)
    
    // Menghapus data
    delete(scores, "Charlie")
    fmt.Println("Scores after delete:", scores)
    
    // Iterasi map
    for name, score := range scores {
        fmt.Printf("%s: %d\n", name, score)
    }
}

Struct

Struct adalah type yang mengelompokkan data dengan nama field:

package main

import "fmt"

// Definisi struct
type Person struct {
    Name    string
    Age     int
    Email   string
    Address Address // nested struct
}

type Address struct {
    Street string
    City   string
    Country string
}

func main() {
    // Membuat instance struct
    var person1 Person
    person1.Name = "Ali"
    person1.Age = 25
    person1.Email = "ali@email.com"
    
    // Struct literal
    person2 := Person{
        Name:  "Budi",
        Age:   30,
        Email: "budi@email.com",
        Address: Address{
            Street:  "Jl. Merdeka 123",
            City:    "Jakarta",
            Country: "Indonesia",
        },
    }
    
    // Positional initialization
    person3 := Person{"Citra", 28, "citra@email.com", Address{}}
    
    fmt.Println("Person1:", person1)
    fmt.Println("Person2:", person2)
    fmt.Println("Person3:", person3)
    
    // Mengakses field
    fmt.Printf("%s berumur %d tahun\n", person2.Name, person2.Age)
    fmt.Printf("Alamat: %s, %s\n", person2.Address.Street, person2.Address.City)
}

Pointer Types

Pointer menyimpan alamat memori dari variable lain:

package main

import "fmt"

func main() {
    var x int = 42
    var p *int = &x  // p adalah pointer ke x
    
    fmt.Println("Nilai x:", x)
    fmt.Println("Alamat x:", &x)
    fmt.Println("Nilai p:", p)
    fmt.Println("Nilai yang ditunjuk p:", *p)
    
    // Mengubah nilai melalui pointer
    *p = 100
    fmt.Println("x setelah diubah melalui pointer:", x)
    
    // Pointer ke struct
    type Point struct {
        X, Y int
    }
    
    point := Point{10, 20}
    pointPtr := &point
    
    fmt.Printf("Point: %v\n", point)
    fmt.Printf("Melalui pointer: X=%d, Y=%d\n", pointPtr.X, pointPtr.Y)
    
    // Mengubah melalui pointer
    pointPtr.X = 30
    fmt.Printf("Point setelah diubah: %v\n", point)
}

Interface Types

Interface mendefinisikan kontrak method tanpa implementasi:

package main

import "fmt"

// Definisi interface
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Struct yang mengimplementasi interface
type Rectangle struct {
    Width, Height float64
}

type Circle struct {
    Radius float64
}

// Method untuk Rectangle
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Method untuk Circle
func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

func main() {
    var s Shape
    
    // Rectangle implements Shape
    s = Rectangle{Width: 10, Height: 5}
    fmt.Printf("Rectangle - Area: %.2f, Perimeter: %.2f\n", 
        s.Area(), s.Perimeter())
    
    // Circle implements Shape
    s = Circle{Radius: 7}
    fmt.Printf("Circle - Area: %.2f, Perimeter: %.2f\n", 
        s.Area(), s.Perimeter())
    
    // Slice of interfaces
    shapes := []Shape{
        Rectangle{Width: 3, Height: 4},
        Circle{Radius: 5},
        Rectangle{Width: 2, Height: 8},
    }
    
    for i, shape := range shapes {
        fmt.Printf("Shape %d - Area: %.2f\n", i+1, shape.Area())
    }
}

Empty Interface

Interface kosong interface{} dapat menyimpan nilai apa saja:

package main

import "fmt"

func main() {
    var anything interface{}
    
    anything = 42
    fmt.Printf("Integer: %v, Type: %T\n", anything, anything)
    
    anything = "Hello World"
    fmt.Printf("String: %v, Type: %T\n", anything, anything)
    
    anything = []int{1, 2, 3}
    fmt.Printf("Slice: %v, Type: %T\n", anything, anything)
    
    // Type assertion
    str, ok := anything.([]int)
    if ok {
        fmt.Println("Type assertion berhasil:", str)
    }
    
    // Type switch
    switch v := anything.(type) {
    case int:
        fmt.Println("Ini adalah integer:", v)
    case string:
        fmt.Println("Ini adalah string:", v)
    case []int:
        fmt.Println("Ini adalah slice of int:", v)
    default:
        fmt.Println("Type tidak diketahui")
    }
}

Channel Types

Channel digunakan untuk komunikasi antar goroutine:

package main

import (
    "fmt"
    "time"
)

func main() {
    // Membuat channel
    ch := make(chan string)
    numbers := make(chan int, 3) // buffered channel
    
    // Mengirim data ke channel (dalam goroutine)
    go func() {
        ch <- "Hello dari goroutine"
    }()
    
    // Menerima data dari channel
    message := <-ch
    fmt.Println("Pesan:", message)
    
    // Buffered channel
    numbers <- 1
    numbers <- 2
    numbers <- 3
    
    fmt.Println("Number 1:", <-numbers)
    fmt.Println("Number 2:", <-numbers)
    fmt.Println("Number 3:", <-numbers)
    
    // Channel dengan select
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "dari ch1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "dari ch2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Menerima:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Menerima:", msg2)
        }
    }
}

Function Types

Function juga merupakan type di Go:

package main

import "fmt"

// Function type
type Operation func(int, int) int

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

func multiply(a, b int) int {
    return a * b
}

func calculate(op Operation, x, y int) int {
    return op(x, y)
}

func main() {
    // Function sebagai variable
    var operation Operation = add
    result1 := operation(5, 3)
    
    // Function sebagai parameter
    result2 := calculate(multiply, 4, 6)
    
    // Anonymous function
    subtract := func(a, b int) int {
        return a - b
    }
    result3 := calculate(subtract, 10, 4)
    
    fmt.Println("Addition result:", result1)     // 8
    fmt.Println("Multiplication result:", result2) // 24
    fmt.Println("Subtraction result:", result3)   // 6
    
    // Function sebagai return value
    getOperation := func(name string) Operation {
        switch name {
        case "add":
            return add
        case "multiply":
            return multiply
        default:
            return subtract
        }
    }
    
    op := getOperation("add")
    result4 := op(7, 2)
    fmt.Println("Dynamic operation result:", result4) // 9
}

Type Conversion

Go memerlukan explicit conversion antar type:

package main

import "fmt"

func main() {
    var i int = 42
    var f float64 = float64(i)  // int ke float64
    var u uint = uint(f)        // float64 ke uint
    
    fmt.Printf("int: %d, float64: %.2f, uint: %d\n", i, f, u)
    
    // String conversion
    var s string = fmt.Sprintf("%d", i)  // int ke string
    fmt.Printf("string: %s\n", s)
    
    // Byte slice ke string dan sebaliknya
    text := "Hello World"
    bytes := []byte(text)        // string ke []byte
    backToString := string(bytes) // []byte ke string
    
    fmt.Printf("Original: %s\n", text)
    fmt.Printf("Bytes: %v\n", bytes)
    fmt.Printf("Back to string: %s\n", backToString)
    
    // Type assertion untuk interface
    var anything interface{} = "Hello"
    str, ok := anything.(string)
    if ok {
        fmt.Printf("Type assertion berhasil: %s\n", str)
    }
}

Zero Values

Setiap type memiliki zero value default:

package main

import "fmt"

func main() {
    // Basic types zero values
    var i int
    var f float64
    var b bool
    var s string
    
    // Composite types zero values
    var arr [3]int
    var slice []int
    var m map[string]int
    var p *int
    var ch chan int
    
    // Struct zero value
    type Person struct {
        Name string
        Age  int
    }
    var person Person
    
    fmt.Printf("int: %d\n", i)         // 0
    fmt.Printf("float64: %.1f\n", f)   // 0.0
    fmt.Printf("bool: %t\n", b)        // false
    fmt.Printf("string: '%s'\n", s)    // ""
    fmt.Printf("array: %v\n", arr)     // [0 0 0]
    fmt.Printf("slice: %v\n", slice)   // []
    fmt.Printf("map: %v\n", m)         // map[]
    fmt.Printf("pointer: %v\n", p)     // <nil>
    fmt.Printf("channel: %v\n", ch)    // <nil>
    fmt.Printf("struct: %v\n", person) // { 0}
    
    // Cek nil
    fmt.Printf("slice == nil: %t\n", slice == nil)
    fmt.Printf("map == nil: %t\n", m == nil)
    fmt.Printf("pointer == nil: %t\n", p == nil)
    fmt.Printf("channel == nil: %t\n", ch == nil)
}

Best Practices

1. Pilih Type yang Tepat

// Gunakan int untuk counter/index umum
for i := 0; i < 10; i++ {
    // ...
}

// Gunakan uint8 untuk byte data
var pixels []uint8

// Gunakan float64 untuk scientific calculation
var pi float64 = 3.141592653589793

// Gunakan bool untuk flag
var isActive bool = true

2. Gunakan Slice daripada Array untuk Fleksibilitas

// Lebih fleksibel
func processItems(items []string) {
    // ...
}

// Kurang fleksibel
func processItems(items [10]string) {
    // ...
}

3. Inisialisasi Map dan Slice

// BAIK: inisialisasi eksplisit
m := make(map[string]int)
s := make([]int, 0)

// BURUK: menggunakan nil map/slice langsung
var m map[string]int
// m["key"] = 1  // panic: assignment to entry in nil map

4. Gunakan Struct untuk Data yang Terkait

// BAIK: grouped data
type User struct {
    ID    int
    Name  string
    Email string
}

// BURUK: separate variables
var userID int
var userName string
var userEmail string

Latihan

Latihan 1: Basic Types

package main

import "fmt"

func main() {
    // TODO: Deklarasikan variable dengan berbagai basic type:
    // - umur (int): 25
    // - tinggi (float64): 175.5
    // - nama (string): "Andi"
    // - lulus (bool): true
    // Tampilkan semua variable dan typenya
}

Latihan 2: Slice Operations

package main

import "fmt"

func main() {
    // TODO: Buat slice angka dari 1-10
    // Tampilkan:
    // - 3 elemen pertama
    // - 3 elemen terakhir
    // - elemen tengah (index 4-6)
    // - semua elemen genap
}

Latihan 3: Map Operations

package main

import "fmt"

func main() {
    // TODO: Buat map untuk menyimpan data siswa
    // Key: nama siswa (string)
    // Value: nilai (int)
    // Tambahkan minimal 5 siswa
    // Tampilkan siswa dengan nilai tertinggi dan terendah
}

Latihan 4: Struct dan Method

package main

import "fmt"

// TODO: Buat struct Book dengan field:
// - Title (string)
// - Author (string)  
// - Pages (int)
// - Price (float64)

// TODO: Buat method untuk struct Book:
// - Info() string: return informasi lengkap buku
// - IsExpensive() bool: return true jika harga > 100000

func main() {
    // TODO: Buat beberapa instance Book
    // Test semua method yang sudah dibuat
}

Kesimpulan

Data types adalah fondasi dari programming di Go. Poin-poin penting:

  • Go memiliki sistem type yang kuat dan static
  • Basic types: int, float, bool, string
  • Composite types: array, slice, map, struct
  • Reference types: pointer, interface, function, channel
  • Setiap type memiliki zero value yang aman
  • Type conversion harus dilakukan secara eksplisit
  • Interface memberikan fleksibilitas dan polymorphism

Dengan memahami berbagai data type di Go, kita dapat memilih type yang tepat untuk kebutuhan program dan menulis kode yang efisien serta mudah dipelihara. Di bab selanjutnya, kita akan belajar tentang operators dan expressions untuk memanipulasi data-data ini.