Pendahuluan

Sejarah Go Lang

  • Dibuat di Google menggunakan bahasa pemrograman C
  • Di Rilis ke public sebagai open source pada tahun 2009
  • Go-Lang populer sejak digunakan untuk membuat Docker pada tahun 2011
  • Saat ini mulai banyak teknologi baru yang dibuat menggunakan bahasa Go-Lang dibanding bahasa C, seperti Kubernetes, Prometheus, CockroachDB, dan lain-lain
  • Saat ini mulai populer untuk pembuatan Backend API di Microservices

Kenapa belajar golang?

  • Bahasa Go-Lang sangat sederhana, tidak butuh waktu lama untuk mempelajarinya
  • Go-Lang mendukung baik concurrency programming, dimana saat ini kita hidup di zaman multicore processor
  • Go-Lang mendukung garbage collector, sehingga tidak butuh melakukan management memory secara manual seperti di bahasa C
  • Salah satu bahasa pemrograman yang sedang naik daun

Proses Development Program Go-Lang

Setelah menulis kode, akan dicompile menggunakan go compiler dan akan menghasilkan binary file.

Prerequisite

Menginstall Go-Lang

Text Editor atau IDE

Membuat Project Go

  • Project di Go-Lang, biasanya disebut sebagai module
  • Untuk membuat module, kita bisa menggunakan perintah berikut di folder tempat kita akan membuat module : go mod init nama-module
  • Fitur Go-Lang Modules sebenarnya masih banyak, namun akan kita bahas di kelas khusus membahas tentang Go-Lang Modules

Hello World

  • Go-Lang, itu mirip seperti bahasa pemrograman C/C++, dimana perlu ada yang namanya main function
  • Main function adalah sebuah fungsi yang akan dijalankan ketika program berjalan
  • Untuk membuat function, kita bisa menggunakan kata kunci func
  • Main function harus terdapat didalam main package
  • Titik koma di Golang, tidaklah wajib, artinya kita bisa menambahkan titik koma atau tidak, diakhir kode program kita

Println

  • Untuk menulis tulisan, kita perlu melakukan import module fmt terlebih dahulu
  • Mirip ketika kita belajar Java
package main
 
import "fmt"
 
func main() {
	fmt.Println("Helo world")
}

Untuk build jalankan perintah

$ go build

akan mengenerate file sesuai dengan nama project/modulenya, jalankan dengan perintah

$ ./dasar # (dasar adalah nama project)

Menjalankan Tanpa Kompilasi

Golang bisa menjalankan langsung program tanpa harus di-compile terlebih dahulu dengan command:

$ go run helloworld.go
# Helo world (output)
  • Tapi hanya untuk kebutuhan development, disarankan dicompile terlebih dahulu.

Multiple Main Function

  • Di Golang, function dalam module / project adalah unik, artinya kita tidak boleh membuat nama function yang sama
  • Oleh karena itu, jika kita membuat file baru, misal sample.go, lalu membuat nama function yang sama yaitu main
  • Maka kita tidak bisa melakukan build module, karena main function tersebut duplikat dengan yang ada di main function helloworld.go contohnya adalah seperti berikut ini other declaration of …
  • Karena masih belajar kita akan menjalankan file golang satu persatu. menggunakan go run

Tipe Data

Number

Ada dua jenis tipe data Number, yaitu :

  • Integer
  • Floating Point
Tipe DataNilai MinimumNilai Maksimum
int8-128127
int16-3276832767
int32-21474836482147483647
int64-92233720368547758089223372036854775807

Gunakan tipe data number yang sesuai dengan kebutuhan, jika semua menggunakan yang paling besar nanti ukuran yang dibutuhkan memori juga ikut besar.

Tipe DataNilai MinimumNilai Maksimum
uint80255
uint16065535
uint3204294967295
uint64018446744073709551615

uint atau unsigned integer adalah tipe data yang tidak memiliki angka negatif.

Tipe DataNilai MinimumNilai Maksimum
float321.18×10−383.4×1038
float642.23×10−3081.80×10308
complex64complex numbers with float32 real and imaginary parts.
complex128complex numbers with float64 real and imaginary parts.

Tipe data diatas jika angka kita mengandung nilai koma atau desimal. Untuk tipe data complex jarang digunakan kecuali aplikasi yang membutuhkan matematik yang kompleks.

Tipe DataAlias untuk
byteuint8
runeint32
intMinimal int32
uintMinimal uint32

Tipe data alias adalah nama lain dari tipe data number yang ada contohnya seperti diatas.

package main
 
import "fmt"
 
func main() {
	fmt.Println("Satu", 1)
	fmt.Println("Dua", 2)
	fmt.Println("Tiga koma lima", 3.5)
}

Angka 1 dan 2 akan otomatis dikonversi menjadi tipe data int32 dan untuk angka 3.5 akan menjadi tipe data float32

Boolean

  • Tipe data boolean adalah tipe data yang memiliki nilai dua nilai, yaitu benar atau salah
  • Di Go-Lang, tipe data boolean direpresentasikan menggunakan kata kunci bool
Nilai BooleanKeterangan
trueBenar
falseSalah
package main
 
import "fmt"
 
func main() {
	fmt.Println("Benar", true)
	fmt.Println("Salah", false)
}

String

  • String ada tipe data kumpulan karakter
  • Jumlah karakter di dalam String bisa nol sampai tidak terhingga
  • Tipe data String di Go-Lang direpresentasikan dengan kata kunci string
  • Nilai data String di Go-Lang selalu diawali dengan karakter “ (petik dua) dan diakhiri dengan karakter “ (petik dua)
package main
 
import "fmt"
 
func main() {
	fmt.Println("Rahmat")
	fmt.Println("Rahmat Ardiansyah")
}

Function untuk String

FunctionKeterangan
len(“string”)Menghitung jumlah karakter di String
“string”[number]Mengambil karakter pada posisi yang ditentukan
package main
 
import "fmt"
 
func main() {
	fmt.Println("Rahmat")
	fmt.Println("Rahmat Ardiansyah")
	fmt.Println(len("Rahmat Ardiansyah"))
}

Outputnya adalah

Rahmat
Rahmat Ardiansyah
17
package main
 
import "fmt"
 
func main() {
	fmt.Println("Rahmat")
	fmt.Println("Rahmat Ardiansyah")
	fmt.Println(len("Rahmat Ardiansyah"))
	fmt.Println("Rahmat Ardiansyah"[0])
}

Outputnya adalah

Rahmat
Rahmat Ardiansyah
17
82
  • Kenapa untuk yang mengambil huruf tertentu di string mengembalikan angka? golang akan mengembalikan nilai byte, harus di-conversi lagi ke tipe data string.

Variable

  • Variable adalah tempat untuk menyimpan data
  • Variable digunakan agar kita bisa mengakses data yang sama dimanapun kita mau
  • Di Go-Lang Variable hanya bisa menyimpan tipe data yang sama, jika kita ingin menyimpan data yang berbeda-beda jenis, kita harus membuat beberapa variable
  • Untuk membuat variable, kita bisa menggunakan kata kunci var, lalu diikuti dengan nama variable dan tipe datanya

contohnya:

package main
 
import "fmt"
 
func main() {
	var name string
	name = "rahmat"
	fmt.Println(name)
	name = "Ardiansyah"
	fmt.Println(name)
}

Outputnya

➜ go run variable.go 
rahmat
Ardiansyah
  • Variable name akan ditimpa dengan value baru

Tipe Data Variable

  • Saat kita membuat variable, maka kita wajib menyebutkan tipe data variable tersebut, jika tidak bisa terjadi Error
  • Namun jika kita langsung menginisialisasikan data pada variable nya, maka kita tidak wajib menyebutkan tipe data variable nya

Kata Kunci Var

  • Di Golang, kata kunci var saat membuat variable tidak lah wajib.
  • Asalkan saat membuat variable kita langsung menginisialisasi datanya
  • Agar tidak perlu menggunakan kata kunci var, kita perlu menggunakan kata kunci := saat menginisialisasikan data pada variable tersebut
package main
 
import "fmt"
 
func main() {
	name := "rahmat"
	fmt.Println(name)
	name = "Ardiansyah"
	fmt.Println(name)
}
  • Jika kita ingin mengubah value dari variable name kita tidak boleh lagi menggunakan : hanya ’=’, jika ada tanda : maka akan terjadi error Error

Deklarasi Multiple Variable

  • Di Go-Lang kita bisa membuat variable secara sekaligus banyak
  • Code yang dibuat akan lebih bagus dan mudah dibaca
package main
 
import "fmt"
 
func main() {
	var (
		firstName = "Rahmat"
		lastName  = "Ardiansyah"
	)
	fmt.Println(firstName, lastName)
}
➜ go run variable.go 
Rahmat Ardiansyah
  • Jika menggunakan function Println seperti diatas akan terdapat spasi langsung.
  • Jika variable firstName atau lastName maka akan terjadi Error.

Constant

  • Constant adalah variable yang nilainya tidak bisa diubah lagi setelah pertama kali diberi nilai
  • Cara pembuatan constant mirip dengan variable, yang membedakan hanya kata kunci yang digunakan adalah const, bukan var
  • Saat pembuatan constant, kita wajib langsung menginisialisasikan datanya, jika tidak akan terjadi Error
package main
 
import "fmt"
 
func main() {
	const name = "rahmat"
}
  • Jika menggunakan constant kita tidak wajib menggunakan variabel tersebut, berbeda dengan Var

Deklarasi Multiple Constant

  • Sama seperti variable, di Go-Lang juga kita bisa membuat constant secara sekaligus banyak
package main
 
func main() {
	const (
		firstName = "Rahmat"
		lastName  = "Ardiansyah"
	)
}

Konversi Tipe Data

  • Di Go-Lang kadang kita butuh melakukan konversi tipe data dari satu tipe ke tipe lain
  • Misal kita ingin mengkonversi tipe data int32 ke int63, dan lain-lain
package main
 
import "fmt"
 
func main() {
	var varA int32 = 32768
	var varB int64 = int64(varA)
	var varC int16 = int16(varB)
	fmt.Println(varA)
	fmt.Println(varB)
	fmt.Println(varC)
}
➜ go run conversion.go 
32768
32768
-32768
  • Untuk varB sudah berhasil di konversi menjadi int64, tapi tidak dengan varC
  • Ketika kita mengkonversi number ke yang lebih rendah jangkauannya hati-hati terjadi number overflow, seperti yang terjadi pada varC.
  • nilai varC menjadi negatif karena maksimal dari int16 adalah 32767, jika lewat dari nilai maksimal akan meng-ulang dari dari nilai minimumnya dan menambahkan selebihnya.
package main
 
import "fmt"
 
func main() {
	var name = "rahmat"
	var hurufT = name[5] // byte
	var StringT = string(hurufT)
	fmt.Println(StringT) // t
}
  • Code diatas untuk mengubah nilai string dari dari string menjadi string

Type Declarations

  • Type Declarations adalah kemampuan membuat ulang tipe data baru dari tipe data yang sudah ada
  • Type Declarations biasanya digunakan untuk membuat alias terhadap tipe data yang sudah ada, dengan tujuan agar lebih mudah dimengerti
  • Type juga bisa digunakan untuk konversi value
package main
 
import "fmt"
 
func main() {
	type noKTP string
 
	var KTP1 noKTP = "111111111111"
	fmt.Println(KTP1)
 
	// menggunakan type noKTP
	fmt.Println(noKTP("222222222222"))
}

Operasi Matematika

OperatorKeterangan
+Penjumlahan
-Pengurangan
*Perkalian
/Pembagian
%Sisa Pembagian
package main
 
import "fmt"
 
func main() {
	var a = 10
	var b = 30
	var c = a + b
	fmt.Println(c) // 40
}

Augmented Assignments

Operasi MatematikaAugmented Assignments
a = a + 10a += 10
a = a - 10a -= 10
a = a * 10a *= 10
a = a / 10a /= 10
a = a % 10a %= 10
package main
 
import "fmt"
 
func main() {
	var a = 10
	a += 20
	fmt.Println(a) // 30
}

Unary Operator

OperatorKeterangan
++a = a + 1
a = a - 1
-Negative
+Positive
!Boolean kebalikan
package main
 
import "fmt"
 
func main() {
	var b = 2
	b++
	b++
 
	fmt.Println(b) // 4
}

Operasi Perbandingan

  • Operasi perbandingan adalah operasi untuk membandingkan dua buah data
  • Operasi perbandingan adalah operasi yang menghasilkan nilai boolean (benar atau salah)
  • Jika hasil operasinya adalah benar, maka nilainya adalah true
  • Jika hasil operasinya adalah salah, maka nilainya adalah false
OperatorKeterangan
>Lebih Dari
<Kurang Dari
>=Lebih Dari Sama Dengan
Kurang Dari Sama Dengan
==Sama Dengan
!=Tidak Sama Dengan
package main
 
import "fmt"
 
func main() {
	var a = 20
	var b = 10
	fmt.Println(a > b) // true
}

Operasi Boolean

OperatorKeterangan
&&Dan
|Atau
!Kebalikan

Operasi &&

Nilai 1OperatorNilai 2Hasil
true&&truetrue
true&&falsefalse
false&&truefalse
false&&falsefalse

Operasi ||

Nilai 1OperatorNilai 2Hasil
true|truetrue
true|falsetrue
false|truetrue
false|falsefalse

Operasi !

OperatorNilai 2Hasil
!truefalse
!falsetrue
package main
 
import "fmt"
 
func main() {
 
	var nilaiAkhir = 90
	var absensi = 70
 
	var lulusNilaiAkhir = nilaiAkhir > 80
	var lulusAbsensi = absensi > 70
 
	var status = lulusNilaiAkhir && lulusAbsensi
	fmt.Println(status)
}

Tipe Data Array

  • Array adalah tipe data yang berisikan kumpulan data dengan tipe yang sama
  • Saat membuat array, kita perlu menentukan jumlah data yang bisa ditampung oleh Array tersebut
  • Daya tampung Array tidak bisa bertambah setelah Array dibuat, jika lebih akan terjadi Error

Index di Array

IndexData
0Eko
1Kurniawan
2Khannedy
package main
 
import "fmt"
 
func main() {
	var names [2]string
 
	names[0] = "rahmat"
	names[1] = "ardiansyah"
 
	fmt.Println(names)
}
➜ go run array.go 
[rahmat ardiansyah]
  • outputnya tidak mempunyi simbol , seperti di Javascript

Array

Bagaimana cara membuat array yang tidak diketahui maksimum jumlah elementnya Jawabannya gunakan Tipe Data Slice

Membuat Array Langsung

  • Di Golang kita juga bisa membuat Array secara langsung saat deklarasi variable
package main
 
import "fmt"
 
func main() {
	// var numbers = [3]int{1, 2, 3} // tidak perlu koma
	var numbers = [3]int{
		1, 2, 3,
	}
 
	fmt.Println(numbers) // [1 2 3]
}

Catatan

Jika menggunakan Enter, element yang diakhir harus memiliki koma contohnya: 1, 2, 3,

package main
 
import "fmt"
 
func main() {
 
	var numbers = [3]int{
		1, 2,
	}
 
	fmt.Println(numbers) // [1, 2, 0]
}
  • Jika element di array nya jumlanya kurang dari deklarasinya maka akan mengembalikan default value. Jika number 0 dan jika string maka string kosong "".

Function Array

OperasiKeterangan
len(array)Untuk mendapatkan panjang Array
array[index]Mendapat data di posisi index
array[index] = valueMengubah data di posisi index
  • Array tidak memiliki fungsi untuk menghapus element
package main
 
import "fmt"
 
func main() {
 
	var numbers = [3]int{1, 2, 3}
	
	fmt.Println(len(numbers))
	fmt.Println(numbers[1])
	numbers[2] = 10
	fmt.Println(numbers)
}
➜ go run array.go 
3
2
[1 2 10]

Element array yang panjangnya dinamis

package main
 
import "fmt"
 
func main() {
	var numbers = [...]int{1, 2, 3, 4, 5, 6} // dan seterusnya
	fmt.Println(len(numbers)) // 6
}
  • Untuk membuat array dengan panjang dinamis gunakan kata kunci ... seperti diatas.
package main
 
import "fmt"
 
func main() {
	var numbers = [...]int{1, 2, 3, 4, 5, 6} // dan seterusnya
	fmt.Println(len(numbers))
	var names = [...]string // error
}

Array

Tapi tidak bisa dilakukan tanpa deklarasi valuenya seperti diatas dan akan menghasilkan Error

Tipe Data Slice

  • Tipe data Slice adalah potongan dari data Array

  • Slice mirip dengan Array, yang membedakan adalah ukuran Slice bisa berubah

  • Slide dan Array selalu terkoneksi, dimana Slice adalah data yang mengakses sebagian atau seluruh data di Array (artinya didalam slice terdapat array yang diatur oleh slice).

  • Tipe Data Slice memiliki 3 data, yaitu pointer, length dan capacity

    • Pointer adalah penunjuk data pertama di array para slice
    • Length adalah panjang dari slice, dan
    • Capacity adalah kapasitas dari slice, dimana length tidak boleh lebih dari capacity

Membuat Slice dari Array

Membuat SliceKeterangan
array[low:high]Membuat slice dari array dimulai index low sampai index sebelum high(atau -1*)
array[low:]Membuat slice dari array dimulai index low sampai index akhir di array
array[:high]Membuat slice dari array dimulai index 0 sampai index sebelum high (atau -1*)
array[:]Membuat slice dari array dimulai index 0 sampai index akhir di array

package main
 
import "fmt"
 
func main() {
	names := [...]string{"jhon", "jane", "doe", "mike", "josh", "rahmat", "ardiansyah"}
	var slice5 []string = names[:]
	fmt.Println(slice5)
}
  • Kode diatas adalah cara membuat slice menggunakan keyword var
  • Kita tidak mendefinisikan panjang dari slice saat membuat variabel
package main
 
import "fmt"
 
func main() {
	names := [...]string{"jhon", "jane", "doe", "mike", "josh", "rahmat", "ardiansyah"}
 
	// "mike", "josh", "rahmat"
	slice1 := names[3:6]
	fmt.Println(slice1)
	// "jane", "doe", "mike", "josh", "rahmat", "ardiansyah"
	slice2 := names[2:]
	fmt.Println(slice2)
	// "jhon", "jane", "doe", "mike", "josh",
	slice3 := names[:5]
	fmt.Println(slice3)
	// "jhon", "jane", "doe", "mike", "josh", "rahmat", "ardiansyah"
	slice4 := names[:]
	fmt.Println(slice4)
}

Function pada slice

OperasiKeterangan
len(slice)Untuk mendapatkan panjang
cap(slice)Untuk mendapat kapasitas
append(slice, data)Membuat slice baru dengan menambah data ke posisi terakhir slice, jika kapasitas sudah penuh, maka akan membuat array baru
make([]TypeData, length, capacity)Membuat slice baru
copy(destination, source)Menyalin slice dari source ke destination
package main
 
import "fmt"
 
func main() {
	days := [...]string{"senin", "selasa", "rabu", "kamis", "jumat", "sabtu", "minggu"}
 
	daySlice1 := days[5:]
	fmt.Println(daySlice1)
	daySlice1[0] = "Sabtu Baru"
	daySlice1[1] = "Minggu Baru"
	fmt.Println(daySlice1)
}
➜ go run slice.go
[sabtu minggu]
[Sabtu Baru Minggu Baru]
[senin selasa rabu kamis jumat Sabtu Baru Minggu Baru]
  • Jika mengubah element di slice, maka element pada array days juga ikut berubah
package main
 
import "fmt"
 
func main() {
	days := [...]string{"senin", "selasa", "rabu", "kamis", "jumat", "sabtu", "minggu"}
 
	daySlice1 := days[5:]
	fmt.Println(daySlice1)
	daySlice1[0] = "SABTU BARU"
	daySlice1[1] = "MINGGU BARU"
	fmt.Println(daySlice1)
	fmt.Println(days) // berubah
 
	daySlice2 := append(daySlice1, "LIBUR BARU")
	fmt.Println(daySlice1)
	fmt.Println(daySlice2) // membuat data baru
	fmt.Println(days)      // tidak barubah
}
  • Jika menggunakan append maka data lama tidak berubah, dan membuat array baru
package main
 
import "fmt"
 
func main() {
	var newSlice []string = make([]string, 2, 5)
	newSlice[0] = "HALO"
	newSlice[1] = "DUNIA"
 
	fmt.Println(newSlice)      // [HALO DUNIA]
	fmt.Println(cap(newSlice)) // 5
}
  • Make digunakan untuk membuat slice baru yang datanya bukan dari array contohnya seperti diatas.
  • Jika kita ingin menambahkan data pada slice newSlice gunakan append bukan deklarasi menggunakan index seperti index 0 dan 1 diatas. Jika tidak akan terjadi Error
package main
 
import "fmt"
 
func main() {
	days := [...]string{"senin", "selasa", "rabu", "kamis", "jumat", "sabtu", "minggu"}
 
	fromSlice := days[:]
	toSlice := make([]string, len(fromSlice), cap(fromSlice)) // siapkan tipe element, panjang dan kapasitasnya
 
	copy(toSlice, fromSlice) // jangan terbalik (destinasi tujuan, sumber)
	fmt.Println(toSlice)
}
  1. Untuk meng-copy slice dari fromSlice yang pertama dilakukan menyiapakn wadah atau variable slice baru
  2. gunakan function make (tipe element, panjang, dan kapasitas)
  3. gunakan function copy
    1. argument yang pertama adalah slice baru atau destinasi tujuan toSlice
    2. argument yang kedua adalah sumber slice yang ingin di copy fromSlice

Copy Slice

Jangan terbalik mengirimkan argument, jika tidak akan membuat slice baru menjadi kosong

Hati-Hati Saat Membuat Array

  • Saat membuat Array, kita harus berhati-hati, jika salah, maka yang kita buat bukanlah Array, melainkan Slice ataupun kebalikannya.
package main
 
import "fmt"
 
func main() {
	// membuat array
	newArr := [5]int{1, 2, 3, 4, 5}
	newArr2 := [...]int{1, 2, 3, 4, 5}
	// membuat slice
	newSlice := []int{1, 2, 3, 4, 5}
	fmt.Println(newArr, newArr2, newSlice)
}
  • Jika kita menentukan panjangnya maka akan membuat Array
  • Jika tidak menentukan panjangnya maka akan membuat Slice

Tipe Data Map

  • Pada Array atau Slice, untuk mengakses data, kita menggunakan index Number dimulai dari 0
  • Map adalah tipe data lain yang berisikan kumpulan data yang sama, namun kita bisa menentukan jenis tipe data index yang akan kita gunakan
  • Sederhananya, Map adalah tipe data kumpulan key-value (kata kunci - nilai), dimana kata kuncinya bersifat unik, tidak boleh sama
  • Berbeda dengan Array dan Slice, jumlah data yang kita masukkan ke dalam Map boleh sebanyak-banyaknya, asalkan kata kunci nya berbeda, jika kita gunakan kata kunci sama, maka secara otomatis data sebelumnya akan diganti dengan data baru
package main
 
import "fmt"
 
func main() {
	var newMap map[string]string = map[string]string{
		"name": "rahmat",
		"age":  "24",
	}
 
	fmt.Println(newMap["name"])
	fmt.Println(newMap["age"])
}

Atau cara yang lebih singkatnya adalah:

package main
 
import "fmt"
 
func main() {
 
	newMap := map[string]string{
		"name": "rahmat",
		"age":  "24",
	}
 
	fmt.Println(newMap["name"])
	fmt.Println(newMap["age"])
	fmt.Println(newMap["address"])// kosong karena tidak ada
}
  • Jika kita mengakses key yang tidak ada maka akan mengembalikan nilai kosong.

Function Map

OperasiKeterangan
len(map)Untuk mendapatkan jumlah data di map
map[key]Mengambil data di map dengan key
map[key] = valueMengubah data di map dengan key
make(map[TypeKey]TypeValue)Membuat map baru
delete(map, key)Menghapus data di map dengan key
package main
 
import "fmt"
 
func main() {
	book := make(map[string]string) // bisa juga membuat map seperti ini
 
	book["title"] = "Buku Go-Lang"
	book["author"] = "Eko Kurniawan"
	book["wrong"] = "Ups"
 
	fmt.Println(book)
	delete(book, "wrong")
	fmt.Println(book)
}
  • Coding diatas adalah untuk membuat map baru menggunakan function make
  • Dan menghapus key-value menggunakan function delete

If Expression

  • If adalah salah satu kata kunci yang digunakan untuk percabangan
  • Percabangan artinya kita bisa mengeksekusi kode program tertentu ketika suatu kondisi terpenuhi
  • Hampir di semua bahasa pemrograman mendukung if expression
package main
 
import "fmt"
 
func main() {
	var name = "rahmat"
 
	if name == "rahmat" {
		fmt.Println("Halo Rahmat")
	}
}

Else Expression

  • Blok if akan dieksekusi ketika kondisi if bernilai true
  • Kadang kita ingin melakukan eksekusi program tertentu jika kondisi if bernilai false
  • Hal ini bisa dilakukan menggunakan else expression
package main
 
import "fmt"
 
func main() {
	var name = "ardiansyah"
 
	if name == "rahmat" {
		fmt.Println("Halo Rahmat")
	} else {
		fmt.Println("Halo, boleh kenalan?")
	}
}

Else If Expression

  • Kadang dalam If, kita butuh membuat beberapa kondisi
  • Kasus seperti ini, kita bisa menggunakan Else If expression
package main
 
import "fmt"
 
func main() {
	var name = "ardiansyah"
 
	if name == "rahmat" {
		fmt.Println("Halo Rahmat")
	} else if name == "ardiansyah" {
		fmt.Println("Halo, Ardiansyah")
	} else {
		fmt.Println("Halo, boleh kenalan?")
	}
}

If dengan Short Statement

package main
 
import "fmt"
 
func main() {
	var name = "ardiansyah"
 
	if length := len(name); length > 6 {
		fmt.Println("Nama terlalu panjang")
	}
}
  • Gunakan cara diatas ketika legth tidak dibutuhkan kembali

Switch Expression

  • Selain if expression, untuk melakukan percabangan, kita juga bisa menggunakan Switch Expression
  • Switch expression sangat sederhana dibandingkan if
  • Biasanya switch expression digunakan untuk melakukan pengecekan ke kondisi dalam satu variable
package main
 
import "fmt"
 
func main() {
	name := "eko"
 
	switch name {
	case "rahmat":
		fmt.Println("Halo Rahmat")
	case "ardiansyah":
		fmt.Println("Halo, Ardiansyah")
	default:
		fmt.Println("Halo, boleh kenalan?")
	}
}

Switch dengan Short Statement

  • Sama dengan If, Switch juga mendukung short statement sebelum variable yang akan di cek kondisinya
package main
 
import "fmt"
 
func main() {
	name := "eko"
 
	switch length := len(name); length > 4 {
	case true:
		fmt.Println("Halo eko")
	case false:
		fmt.Println("Nama terlalu panjang")
	}
}

Switch Tanpa Kondisi

  • Kondisi di switch expression tidak wajib
  • Jika kita tidak menggunakan kondisi di switch expression, kita bisa menambahkan kondisi tersebut di setiap case nya
package main
 
import "fmt"
 
func main() {
	name := "rahmat"
 
	length := len(name)
 
	switch {
	case length > 10:
		fmt.Println("Nama terlalu panjang")
	case length > 5:
		fmt.Println("Nama lumayan panjang")
	default:
		fmt.Println("Nama sudah benar")
	}
 
}

For

  • Dalam bahasa pemrograman, biasanya ada fitur yang bernama perulangan
  • Salah satu fitur perulangan adalah for loops
package main
 
import "fmt"
 
func main() {
	counter := 1
 
	for counter <= 10 {
		fmt.Println("Perulangan ke ", counter)
		counter++
	}
}

For dengan Statement

  • Dalam for, kita bisa menambahkan statement, dimana terdapat 2 statement yang bisa tambahkan di for
  • Init statement, yaitu statement sebelum for di eksekusi
  • Post statement, yaitu statement yang akan selalu dieksekusi di akhir tiap perulangan
package main
 
import "fmt"
 
func main() {
	for counter := 1; counter <= 10; counter++ {
		fmt.Println("Perulangan ke ", counter)
	}
}
package main
 
import "fmt"
 
func main() {
	names := []string{"rahmat", "ardiansyah", "eko", "kurniawan"}
 
	for i := 0; i < len(names); i++ {
		fmt.Println(names[i])
	}
}

For Range

  • For bisa digunakan untuk melakukan iterasi terhadap semua data collection
  • Data collection contohnya Array, Slice dan Map
package main
 
import "fmt"
 
func main() {
	names := []string{"rahmat", "ardiansyah", "eko", "kurniawan"}
 
	for index, name := range names {
		fmt.Println("index ke -", index, " = ", name)
	}
}
package main
 
import "fmt"
 
func main() {
	names := []string{"rahmat", "ardiansyah", "eko", "kurniawan"}
 
	for _, name := range names {
		fmt.Println(name)
	}
}
  • Gunakan _ jika tidak butuh index

Break & Continue

  • break & continue adalah kata kunci yang bisa digunakan dalam perulangan
  • Break digunakan untuk menghentikan seluruh perulangan
  • Continue adalah digunakan untuk menghentikan perulangan yang berjalan, dan langsung melanjutkan ke perulangan selanjutnya
package main
 
import "fmt"
 
func main() {
 
	for i := 0; i < 10; i++ {
		if i == 5 {
			break
		}
		fmt.Println(i)
	}
}
package main
 
import "fmt"
 
func main() {
 
	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			continue // ulang ke atas
		}
		fmt.Println(i)
	}
}
  • Coding diatas hanya akan print nilai ganjil, karena jika genap maka lanjut ke perulangan berikutnya(bukan kebawah lagi)

Function

  • Sebelumnya kita sudah mengenal sebuah function yang wajib dibuat agar program kita bisa berjalan, yaitu function main
  • Function adalah sebuah blok kode yang sengaja dibuat dalam program agar bisa digunakan berulang-ulang
  • Cara membuat function sangat sederhana, hanya dengan menggunakan kata kunci func lalu diikuti dengan nama function nya dan blok kode isi function nya
  • Setelah membuat function, kita bisa mengeksekusi function tersebut dengan memanggilnya menggunakan kata kunci nama function nya diikuti tanda kurung buka, kurung tutup
package main
 
import "fmt"
 
func sayHello() {
	fmt.Println("Halo")
}
 
func main() {
	sayHello()
	sayHello()
	sayHello()
}

Function Parameter

  • Saat membuat function, kadang-kadang kita membutuhkan data dari luar, atau kita sebut parameter.
  • Kita bisa menambahkan parameter di function, bisa lebih dari satu
  • Parameter tidaklah wajib, jadi kita bisa membuat function tanpa parameter seperti sebelumnya yang sudah kita buat
  • Namun jika kita menambahkan parameter di function, maka ketika memanggil function tersebut, kita wajib memasukkan data ke parameternya. Jika tidak akan terjadi Error
package main
 
import "fmt"
 
func sayHello() {
	fmt.Println("Halo")
}
 
func sayHelloTo(firstName string, lastName string) {
	fmt.Println("Halo,", firstName, lastName)
}
 
func main() {
	sayHelloTo("rahmat", "ardiansyah") // 
}

Function Return Value

  • Function bisa mengembalikan data
  • Untuk memberitahu bahwa function mengembalikan data, kita harus menuliskan tipe data kembalian dari function tersebut
  • Jika function tersebut kita deklarasikan dengan tipe data pengembalian, maka wajib di dalam function nya kita harus mengembalikan data
  • Untuk mengembalikan data dari function, kita bisa menggunakan kata kunci return, diikuti dengan datanya
package main
 
import "fmt"
 
func getHello(name string) string {
	return "Hello " + name
}
 
func main() {
	var result = getHello("rahmat")
	fmt.Println(result)
	fmt.Println(getHello(("ardiansyah")))
}

Returning Multiple Values

  • Function tidak hanya dapat mengembalikan satu value, tapi juga bisa multiple value
  • Untuk memberitahu jika function mengembalikan multiple value, kita harus menulis semua tipe data return value nya di function
package main
 
import "fmt"
 
func getFullName() (string, string) {
	return "Rahmat", "Ardiansyah"
}
 
func main() {
	fmt.Println(getFullName())
}

Menghiraukan Return Value

  • Multiple return value wajib ditangkap semua value nya
  • Jika kita ingin menghiraukan return value tersebut, kita bisa menggunakan tanda _ (garis bawah)
package main
 
import "fmt"
 
func getFullName() (string, string) {
	return "Rahmat", "Ardiansyah"
}
 
func main() {
	firstName, _ := getFullName()
	_, lastName := getFullName()
	fmt.Println(firstName)
	fmt.Println(lastName)
}
  • Gunakan cara diatas untuk mengambil hanya salah satu dari return value, karena di golang semua variabel harus digunakan

Named Return Values

  • Biasanya saat kita memberi tahu bahwa sebuah function mengembalikan value, maka kita hanya mendeklarasikan tipe data return value di function
  • Namun kita juga bisa membuat variable secara langsung di tipe data return function nya

Contoh Kode dibawah adalah cara normal membuat variable untuk return value:

package main
 
import "fmt"
 
func getCompleteName() (string, string, string) {
	firstName := "Rahmat"
	middleName := ""
	lastName := "Ardiansyah"
 
	return firstName, middleName, lastName
}
 
func main() {
	a, b, c := getCompleteName()
 
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

Kita bisa langsung membuat variable di type return value dengan cara:

package main
 
import "fmt"
 
func getCompleteName() (firstName string, middleName string, lastName string) {
	firstName = "Rahmat"
	middleName = ""
	lastName = "Ardiansyah"
 
	return firstName, middleName, lastName
}
 
func main() {
	a, b, c := getCompleteName()
 
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}
  • Dengan cara diatas sudah tidak perlu lagi membuat variabel baru

Bahkan bisa lebih disingkat lagi, hanya tulis type diakhir jika semua tipe data nya sama:

package main
 
import "fmt"
 
func getCompleteName() (firstName, middleName, lastName string) {
	firstName = "Rahmat"
	middleName = ""
	lastName = "Ardiansyah"
 
	return firstName, middleName, lastName
}
 
func main() {
	a, b, c := getCompleteName()
 
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

Apa kegunaan dari cara menulis kode diatas? agar tidak perlu lagi me-return nilai yang tidak diperlukan contohnya:

package main
 
import "fmt"
 
func getCompleteName() (firstName, middleName, lastName string) {
	firstName = "Rahmat"
	lastName = "Ardiansyah"
 
	return firstName, middleName, lastName
}
 
func main() {
	a, b, c := getCompleteName()
 
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}
  • middleName tidak diperlukan, maka golang akan mengembalikan default value nya yaitu ""

Variadic Function

  • Parameter yang berada di posisi terakhir, memiliki kemampuan dijadikan sebuah varargs
  • Varargs artinya datanya bisa menerima lebih dari satu input, atau anggap saja semacam Array. Sama seperti rest operator di Javascript
  • Apa bedanya dengan parameter biasa dengan tipe data Array?
    • Jika parameter tipe Array, kita wajib membuat array terlebih dahulu sebelum mengirimkan ke function
    • JIka parameter menggunakan varargs, kita bisa langsung mengirim data nya, jika lebih dari satu, cukup gunakan tanda koma
package main
 
import "fmt"
 
func sumAllNum(numbers ...int) int {
	total := 0
	for _, number := range numbers {
		total += number
	}
 
	return total
 
}
func main() {
 
	fmt.Println(sumAllNum(sumAllNum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))) // 55
 
}

Slice Parameter

  • Kadang ada kasus dimana kita menggunakan Variadic Function, namun memiliki variable berupa slice
  • Kita bisa menjadikan slice sebagai vararg parameter, seperti spread operator di Javascript
package main
 
import "fmt"
 
func sumAllNum(numbers ...int) int {
	total := 0
	for _, number := range numbers {
		total += number
	}
 
	return total
 
}
func main() {
 
	fmt.Println(sumAllNum(sumAllNum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
 
	numbers := []int{1, 2, 3, 4, 5, 6}
 
	fmt.Println(sumAllNum(numbers...))
 
}

Function Value

  • Function adalah first class citizen
  • Function juga merupakan tipe data, dan bisa disimpan di dalam variable
  • Kegunaannya kita bisa mengirimkan function ke argument Function as Parameter
package main
 
import "fmt"
 
func getGoodBye(name string) string {
	return "Good Bye " + name
}
 
func main() {
	goodBye := getGoodBye
 
	fmt.Println(goodBye("Rahmat"))
}

Function Value

Jangan menggunakan (), karena akan memanggil function tersebut secara langsung

Function as Parameter

  • Function tidak hanya bisa kita simpan di dalam variable sebagai value
  • Namun juga bisa kita gunakan sebagai parameter untuk function lain
package main
 
import "fmt"
 
func sayHelloWithFilter(name string, filter func(string) string) {
	filteredName := filter(name)
 
	fmt.Println("Halo ", filteredName)
}
 
func spamFilter(name string) string {
	if name == "anjing" || name == "anjing" {
		return "***"
	} else {
		return name
	}
}
 
func main() {
	name := "anjing"
 
	sayHelloWithFilter(name, spamFilter)
}

Function Type Declarations

  • Kadang jika function terlalu panjang, agak ribet untuk menuliskannya di dalam parameter
  • Type Declarations juga bisa digunakan untuk membuat alias function, sehingga akan mempermudah kita menggunakan function sebagai parameter
package main
 
import "fmt"
 
type Filter func(string) string
 
func sayHelloWithFilter(name string, filter Filter) {
	filteredName := filter(name)
 
	fmt.Println("Halo ", filteredName)
}
 
func spamFilter(name string) string {
	if name == "anjing" || name == "anjing" {
		return "***"
	} else {
		return name
	}
}
 
func main() {
	name := "anjing"
 
	sayHelloWithFilter(name, spamFilter)
}

Anonymous Function

  • Sebelumnya setiap membuat function, kita akan selalu memberikan sebuah nama pada function tersebut
  • Namun kadang ada kalanya lebih mudah membuat function secara langsung di variable atau parameter tanpa harus membuat function terlebih dahulu
  • Hal tersebut dinamakan anonymous function, atau function tanpa nama
package main
 
import "fmt"
 
type Blacklist func(string) bool
 
func registerUser(name string, blacklist Blacklist) {
	if blacklist(name) {
		fmt.Println("You are blocked")
	} else {
		fmt.Println("Welcom", name)
	}
}
 
func main() {
	blacklist := func(name string) bool {
		return name == "anjing"
	}
	registerUser("rahmat", blacklist)
	registerUser("anjing", func(name string) bool {
		return name == "anjing"
	})
}

Recursive Function

  • Recursive function adalah function yang memanggil function dirinya sendiri
  • Kadang dalam pekerjaan, kita sering menemui kasus dimana menggunakan recursive function lebih mudah dibandingkan tidak menggunakan recursive function
  • Contoh kasus yang lebih mudah diselesaikan menggunakan recursive adalah Factorial
package main
 
import "fmt"
 
func factorial(value int) int {
	result := 1
	for i := value; i > 0; i-- {
		result *= i
	}
	return result
}
 
func factorialRecursive(value int) int {
	if value == 1 {
		return 1
	} else {
		return value * factorialRecursive(value-1)
	}
}
 
func main() {
	fmt.Println(factorial(3))
	fmt.Println(factorialRecursive(10))
}

Closures

  • Closure adalah kemampuan sebuah function berinteraksi dengan data-data disekitarnya dalam scope yang sama
  • Harap gunakan fitur closure ini dengan bijak saat kita membuat aplikasi
package main
 
import "fmt"
 
func main() {
	counter := 0
	/*
		Kode yang panjang...
	*/
 
	increment := func() {
		fmt.Println("Increment")
		counter++
	}
 
	increment()
	increment()
 
	fmt.Println(counter)
}
  • Artinya kode diatas, ketika menggunakan function tapi data yang diluar function tersebut berubah.

Closures

Jangan menggunakan clousures terlalu sering karena akan membuat bingung membacanya

Defer

  • Defer function adalah function yang bisa kita jadwalkan untuk dieksekusi setelah sebuah function selesai di eksekusi
  • Defer function akan selalu dieksekusi walaupun terjadi error di function yang dieksekusi
  • Untuk menggunakan Defer function gunakan keyword defer sebelum pemanggilan function
package main
 
import "fmt"
 
func logging() {
	fmt.Println("Selesai memanggil function")
}
 
func runApplication() {
	defer logging()
	fmt.Println("Aplikasi Berjalan")
}
 
func main() {
	runApplication()
}
➜ go run defer.go 
Aplikasi Berjalan
Selesai memanggil function

Panic

  • Panic function adalah function yang bisa kita gunakan untuk menghentikan program
  • Panic function biasanya dipanggil ketika terjadi panic pada saat program kita berjalan
  • Saat panic function dipanggil, program akan terhenti, namun defer function tetap akan dieksekusi
package main
 
import "fmt"
 
func endApp() {
	fmt.Println("Aplikasi Berhenti")
}
 
func runApplication(error bool) {
	defer endApp()
 
	if error {
		panic("upps")
	}
 
	fmt.Println("Aplikasi Berjalan")
}
 
func main() {
	runApplication(true)
}
➜ go run panic.go // false
Aplikasi Berjalan
Aplikasi Berhenti
 
learn/go/dasar via 🐹 v1.25.4 
➜ go run panic.go // true 
Aplikasi Berhenti
panic: upps
 
goroutine 1 [running]:
main.runApplication(0x80?)
        /home/rahmat/Documents/learn/go/dasar/panic.go:13 +0x8e
main.main()
        /home/rahmat/Documents/learn/go/dasar/panic.go:20 +0x18
exit status 2

Recover

  • Recover adalah function yang bisa kita gunakan untuk menangkap data panic
  • Dengan recover proses panic akan terhenti, sehingga program akan tetap berjalan
package main
 
import "fmt"
 
func endApp() {
	fmt.Println("Aplikasi Berhenti")
}
 
func runApplication(error bool) {
	defer endApp()
 
	if error {
		panic("upps")
	}
	message := recover()
	fmt.Println("Terjadi panic", message)
	fmt.Println("Aplikasi Berjalan")
}
 
func main() {
	runApplication(true)
}

Recover

Kode diatas adalah cara yang salah menggunakan function recover karena ketika function panic dijalankan program dibawahnya berhenti

Solusinya, gunakan recover function di defer function:

package main
 
import "fmt"
 
func endApp() {
	fmt.Println("Aplikasi Berhenti")
	message := recover()
	fmt.Println("Terjadi panic", message)
}
 
func runApplication(error bool) {
	defer endApp()
 
	if error {
		panic("upps")
	}
 
	fmt.Println("Aplikasi Berjalan")
}
 
func main() {
	runApplication(true)
}
➜ go run panic.go 
Aplikasi Berhenti
panic: upps
 
goroutine 1 [running]:
main.runApplication(0x80?)
        /home/rahmat/Documents/learn/go/dasar/panic.go:13 +0x8e
main.main()
        /home/rahmat/Documents/learn/go/dasar/panic.go:20 +0x18
exit status 2

Komentar

  • Komentar terbaik pada kode adalah kode itu sendiri
  • Saat membuat kode, kita perlu membuat kode semudah mungkin untuk dibaca
  • Namun kadang juga kita butuh menambahkan komentar di kode kita
package main
 
import "fmt"
 
func main() {
	// ini komentar
	fmt.Println("Halo")
 
	/*
	   Multi
	   Line
	   Comment
	*/
}

Struct

  • Struct adalah sebuah template data yang digunakan untuk menggabungkan nol atau lebih tipe data lainnya dalam satu kesatuan
  • Struct biasanya representasi data dalam program aplikasi yang kita buat
  • Data di struct disimpan dalam field
  • Sederhananya struct adalah kumpulan dari field
  • Biasanya struct ditulis dengan Pasca Case
type Customer struct {
	Name, Address string
	Age           int
}

Membuat Data Struct

  • Struct adalah template data atau prototype data
  • Struct tidak bisa langsung digunakan
  • Namun kita bisa membuat data/object dari struct yang telah kita buat
package main
 
import "fmt"
 
type Customer struct {
	Name, Address string
	Age           int
}
 
func main() {
	var customer Customer
 
	customer.Name = "Rahmat Ardiansyah"
	customer.Address = "Indonesia"
	customer.Age = 24
 
	fmt.Println(customer)
	fmt.Println(customer.Age)
}
➜ go run struct.go 
{Rahmat Ardiansyah Indonesia 24}
24

Jika kita tidak mendeklarasi valuenya di struct maka akan mengembalikan default valuenya

package main
 
import "fmt"
 
type Customer struct {
	Name, Address string
	Age           int
}
 
func main() {
	var customer Customer
 
	customer.Name = "Rahmat Ardiansyah"
	customer.Address = "Indonesia"
	// customer.Age = 24
 
	fmt.Println(customer)
	fmt.Println(customer.Age)
}
go run struct.go 
{Rahmat Ardiansyah Indonesia 0}
0

Struct Literals

  • Sebelumnya kita telah membuat data dari struct, namun sebenarnya ada banyak cara yang bisa kita gunakan untuk membuat data dari struct
package main
 
import "fmt"
 
type Customer struct {
	Name, Address string
	Age           int
}
 
func main() {
	joko := Customer{
		Name:    "Joko",
		Address: "Indonesia",
		Age:     30,
	}
	fmt.Println(joko)
 
	budi := Customer{"Budi", "Indonesia", 27} // harus sesuai urutan
	fmt.Println(budi)
}
➜ go run struct.go
{Joko Indonesia 30}
{Budi Indonesia 27}

Struct Method

  • Struct adalah tipe data seperti tipe data lainnya, dia bisa digunakan sebagai parameter untuk function
  • Namun jika kita ingin menambahkan method ke dalam structs, sehingga seakan-akan sebuah struct memiliki function
  • Method adalah function
package main
 
import "fmt"
 
type Customer struct {
	Name, Address string
	Age           int
}
 
func (customer Customer) sayHello(name string) {
	fmt.Println("Halo", name, "nama saya", customer.Name)
}
 
func main() {
	fmt.Println(customer)
	fmt.Println(customer.Age)
 
	joko := Customer{
		Name:    "Joko",
		Address: "Indonesia",
		Age:     30,
	}
 
	fmt.Println(joko)
 
	budi := Customer{"Budi", "Indonesia", 27}
 
	fmt.Println(budi)
 
	joko.sayHello("Rahmat")
	budi.sayHello("Rahmat")
}
➜ go run struct.go
{Joko Indonesia 30}
{Budi Indonesia 27}
Halo Rahmat nama saya Joko
Halo Rahmat nama saya Budi

Interface

  • Interface adalah tipe data Abstract, dia tidak memiliki implementasi langsung
  • Sebuah interface berisikan definisi-definisi method
  • Biasanya interface digunakan sebagai kontrak
KeunggulanPenjelasan
FleksibelTipe mana pun bisa memenuhi interface cukup dengan memiliki method yang sesuai.
ModularImplementasi bisa diganti tanpa mengubah kode yang memakai interface.
Mudah diujiCocok untuk mocking ketika unit test.
Mendukung PolimorfismeFungsi dapat menerima berbagai jenis tipe selama memenuhi interface yang sama.

Implementasi Interface

  • Setiap tipe data yang sesuai dengan kontrak interface, secara otomatis dianggap sebagai interface tersebut
  • Sehingga kita tidak perlu mengimplementasikan interface secara manual
  • Hal ini agak berbeda dengan bahasa pemrograman lain yang ketika membuat interface, kita harus menyebutkan secara eksplisit akan menggunakan interface mana
package main
 
import "fmt"
 
type HasName interface {
	GetName() string
}
 
func sayHello(value HasName) {
	fmt.Println("Halo, nama saya", value.GetName())
}
 
func main() {
	sayHello("Rahmat")
}
➜ go run interface2.go 
# command-line-arguments
./interface2.go:14:11: cannot use "Rahmat" (constant of type string) as HasName value in argument to sayHello: string does not implement HasName (missing method GetName)

Implement Interface

Kode diatas akan error karena pemanggilan function sayHello tidak memenuhi kontrak pada interface HasName

Berikut adalah cara yang benar untuk mengirimkan argument ke function yang memiliki kontrak:

package main
 
import "fmt"
 
type HasName interface {
	GetName() string
}
 
func sayHello(value HasName) {
	fmt.Println("Halo, nama saya", value.GetName())
}
 
type Person struct {
	Name string
}
 
func (person Person) GetName() string {
	return person.Name
}
 
func main() {
	person := Person{"Rahmat"}
	sayHello(person)
}
  • Kita membuat method GetName ke struct Person
package main
 
import "fmt"
 
type HasName interface {
	GetName() string
}
 
func sayHello(value HasName) {
	fmt.Println("Halo, nama saya", value.GetName())
}
 
type Person struct {
	Name string
}
 
func (person Person) GetName() string {
	return person.Name
}
 
type Animal struct {
	Name string
}
 
func (animal Animal) GetName() string {
	return animal.Name
}
 
func main() {
	person := Person{"Rahmat"}
	sayHello(person)
 
	cat := Animal{"Kucing"}
	sayHello(cat)
}
  • Membuat lebih dari 1 struct menggunakan interface HasName

Contoh lainnya cara membuat interface:

package main
 
import "fmt"
 
// Membuat interface
type Speaker interface {
    Speak() string
}
 
// Struct pertama
type Dog struct{}
 
func (d Dog) Speak() string {
    return "Guk Guk"
}
 
// Struct kedua
type Cat struct{}
 
func (c Cat) Speak() string {
    return "Meow"
}
 
// Fungsi yang menerima interface
func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}
 
func main() {
    dog := Dog{}
    cat := Cat{}
 
    MakeSound(dog)
    MakeSound(cat)
}
➜ go run interface.go 
Guk Guk
Meow

Interface Kosong

  • Go-Lang bukanlah bahasa pemrograman yang berorientasi objek
  • Biasanya dalam pemrograman berorientasi objek, ada satu data parent di puncak yang bisa dianggap sebagai semua implementasi data yang ada di bahasa pemrograman tersebut
  • Contoh di Java ada java.lang.Object
  • Untuk menangani kasus seperti ini, di Go-Lang kita bisa menggunakan interface kosong
  • Interface kosong adalah interface yang tidak memiliki deklarasi method satupun, hal ini membuat secara otomatis semua tipe data akan menjadi implementasi nya
  • Interface kosong, juga memiliki type alias bernama any
package main
 
import "fmt"
 
/*
func Ups() interface{} {
	return "Ups"
}
*/
 
func Ups() any {
	// return 1
	// return true
	return "Ups"
}
 
func main() {
 
	var ups any = Ups()
 
	fmt.Println(ups)
}
  • Dengan menggunakan any atau interface kosong kita bisa mengirimkan argument dengan tipe data apapun contohnya seperti Code diatas
  • Ada banyak contoh penggunaan interface kosong di Go-Lang, seperti :
    • fmt.Println(a …interface{})
    • panic(v interface{})
    • recover() interface{}
    • dan lain-lain

Nil

  • Biasanya di dalam bahasa pemrograman lain, object yang belum diinisialisasi maka secara otomatis nilainya adalah null atau nil
  • Berbeda dengan Go-Lang, di Go-Lang saat kita buat variable dengan tipe data tertentu, maka secara otomatis akan dibuatkan default value nya
  • Namun di Go-Lang ada data nil, yaitu data kosong
  • Nil sendiri hanya bisa digunakan di beberapa tipe data, seperti interface, function, map, slice, pointer dan channel. Jika tidak akan terjadi Error
package main
 
import "fmt"
 
func sayHello(name string) map[string]string {
	if name == "" {
		return nil
	} else {
		return map[string]string{
			"name": name,
		}
	}
}
 
func main() {
	fmt.Println(sayHello("Rahmat"))
	fmt.Println(sayHello(""))
}
➜ go run nil.go 
map[name:Rahmat]
➜ go run nil.go 
map[]
  • Kode diatas akan jalan karena nil bisa digunakan pada tipe data map
package main
 
import "fmt"
 
func sayHello(name string) map[string]string {
	if name == "" {
		return nil
	} else {
		return map[string]string{
			"name": name,
		}
	}
}
 
func main() {
	data := sayHello("")
 
	if data == nil {
		fmt.Println("Data Masih Kosong")
	} else {
		fmt.Println(data["name"])
	}
}
➜ go run nil.go 
Data Masih Kosong
  • Kode diatas adalah memastikan variable data adalah nil

Type Assertions

  • Type Assertions merupakan kemampuan merubah tipe data menjadi tipe data yang diinginkan
  • Fitur ini sering sekali digunakan ketika kita bertemu dengan data interface kosong
package main
 
import "fmt"
 
func random() any {
	return "OK"
}
 
func main() {
	var result any = random()
	var resultString string = result.(string)
 
	fmt.Println(resultString)
}
  • Code diatas akan mengubah tipe data any atau interface kosong menjadi tipe data string
  • Tapi hati-hati jika ingin mengubah tipe data any menjadi yang bukan tipe data seharusnya, karena akan terjadi panic

Pointer

Pass by Value

  • Secara default di Go-Lang semua variable itu di passing by value, bukan by reference
  • Artinya, jika kita mengirim sebuah variable ke dalam function, method atau variable lain, sebenarnya yang dikirim adalah duplikasi value nya
package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func main() {
	address1 := Address{"Pekanbaru", "Riau", "Indonesia"}
 
	address2 := address1 // copy value
 
	address2.City = "Bandung"
 
	fmt.Println(address1) // tidak berubah
	fmt.Println(address2) // berubah menjadi Bandung
}
➜ go run pointer.go 
{Pekanbaru Riau Indonesia}
{Bandung Riau Indonesia}

  • Artinya address1 datanya akan di-copy ke address2
  • Agar address2 memiliki data yang sama di memory gunakan pointer

Penggunaan Pointer

  • Pointer adalah kemampuan membuat reference ke lokasi data di memory yang sama, tanpa menduplikasi data yang sudah ada
  • Sederhananya, dengan kemampuan pointer, kita bisa membuat pass by reference

Operator &

  • Untuk membuat sebuah variable dengan nilai pointer ke variable yang lain, kita bisa menggunakan operator & diikuti dengan nama variable nya
package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func main() {
	var address1 Address = Address{"Pekanbaru", "Riau", "Indonesia"}
	var address2 *Address = &address1
 
	// Atau
	// address1 := Address{"Pekanbaru", "Riau", "Indonesia"}
	// address2 := &address1
 
	address2.City = "Bandung"
 
	fmt.Println(address1) // ikut berubah
	fmt.Println(address2) // berubah menjadi Bandung
  • Code diatas juga akan mengubah address1 karena menggunakan pointer
  • Dengan cara diatas akan lebih menghemat memory

Operator *

  • Saat kita mengubah variable pointer, maka yang berubah hanya variable tersebut.
  • Semua variable yang mengacu ke data yang sama tidak akan berubah
  • Jika kita ingin mengubah seluruh variable yang mengacu ke data tersebut, kita bisa menggunakan operator *
Tanpa Operator *

package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func main() {
	var address1 Address = Address{"Pekanbaru", "Riau", "Indonesia"}
	var address2 *Address = &address1
 
	address2.City = "Bandung"
 
	fmt.Println(address1) // ikut berubah
	fmt.Println(address2) // berubah menjadi Bandung
 
	address2 = &Address{"Jakarta", "DKI Jakarta", "Indonesia"} // membuat value baru
	fmt.Println(address1)
	fmt.Println(address2)
}
➜ go run asterisk_operator.go 
{Bandung Riau Indonesia}
&{Bandung Riau Indonesia}
{Bandung Riau Indonesia}
&{Jakarta DKI Jakarta Indonesia}
  • address2 yang paling bawah akan menyimpan value baru
  • tapi address1 masih ikut berubah karena coding address2.City = "Bandung" masih mengarah ke reference address1

Pointer

Tanda & harus tetap digunakan karena sebelumnya sudah menggunakan type pointer,

address2 = Address{"Jakarta", "DKI Jakarta", "Indonesia"}

Menggunakan Operator *

package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func main() {
	var address1 Address = Address{"Pekanbaru", "Riau", "Indonesia"}
	var address2 *Address = &address1
 
	address2.City = "Bandung"
 
	fmt.Println(address1) // ikut berubah
	fmt.Println(address2) // berubah menjadi Bandung
 
	*address2 = Address{"Jakarta", "DKI Jakarta", "Indonesia"} // membuat value baru
	fmt.Println(address1)
	fmt.Println(address2)
}
➜ go run asterisk_operator.go 
{Bandung Riau Indonesia}
&{Bandung Riau Indonesia}
{Jakarta DKI Jakarta Indonesia}
&{Jakarta DKI Jakarta Indonesia}

  • Dengan cara diatas isi dari address1 ikut berubah semua dan tergantikan karena adanya operator *

Operator new

  • Sebelumnya untuk membuat pointer dengan menggunakan operator &
  • Go-Lang juga memiliki function new yang bisa digunakan untuk membuat pointer
  • Namun function new hanya mengembalikan pointer ke data kosong, artinya tidak ada data awal
package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func main() {
	alamat1 := new(Address)
	alamat2 := alamat1
 
	alamat2.City = "Dumai"
 
	fmt.Println(alamat1) // alamat 1 berubah
	fmt.Println(alamat2)
}
➜ go run new.go 
&{Dumai  }
&{Dumai  }

Pointer di Function

  • Saat kita membuat parameter di function, secara default adalah pass by value, artinya data akan di copy lalu dikirim ke function tersebut
  • Oleh karena itu, jika kita mengubah data di dalam function, data yang aslinya tidak akan pernah berubah.
  • Hal ini membuat variable menjadi aman, karena tidak akan bisa diubah
  • Namun kadang kita ingin membuat function yang bisa mengubah data asli parameter tersebut
  • Untuk melakukan ini, kita juga bisa menggunakan pointer di function
  • Untuk menjadikan sebuah parameter sebagai pointer, kita bisa menggunakan operator * di parameternya
package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func ChangeCountry(address Address) {
	address.Country = "Indonesia"
}
 
func main() {
	alamat := Address{}
 
	ChangeCountry(alamat)
 
	fmt.Println(alamat) // tidak berubah { }
}
  • Kode diatas tidak akan mengubah country karena variable alamat bukan pointer
package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func ChangeCountry(address *Address) { // jadikan pointer
	address.Country = "Indonesia"
}
 
func main() {
	alamat := &Address{} // reference ke pointernya
 
	ChangeCountry(alamat)
 
	fmt.Println(alamat)
}
➜ go run function_pointer.go 
&{  Indonesia}

Atau seperti ini jika variabel alamat terlanjur bukan pointer:

package main
 
import "fmt"
 
type Address struct {
	City, Province, Country string
}
 
func ChangeCountry(address *Address) {
	address.Country = "Indonesia"
}
 
func main() {
	alamat := Address{}
 
	ChangeCountry(&alamat)
 
	fmt.Println(alamat)
}

Pointer di Method

  • Walaupun method akan menempel di struct, tapi sebenarnya data struct yang diakses di dalam method adalah pass by value
  • Sangat direkomendasikan menggunakan pointer di method, sehingga tidak boros memory karena harus selalu diduplikasi ketika memanggil method
package main
 
import "fmt"
 
type Man struct {
	Name string
}
 
func (man Man) Merried() {
	man.Name = "Mr. " + man.Name
}
 
func main() {
	rahmat := Man{"Rahmat"}
	rahmat.Merried()
	fmt.Println(rahmat) // {Rahmat}
}
  • Kode diatas belum berhasil menambahakn Mr. pada namanya karena kita menerima value di function hasil duplikasi, bukan pass by reference.

Solusinya jadikan method pointer:

package main
 
import "fmt"
 
type Man struct {
	Name string
}
 
func (man *Man) Merried() { // jadikan pointer
	man.Name = "Mr. " + man.Name
}
 
func main() {
	rahmat := Man{"Rahmat"}
	rahmat.Merried()
	fmt.Println(rahmat) // {Mr. Rahmat}
}

Package

  • Package adalah tempat yang bisa digunakan untuk mengorganisir kode program yang kita buat di Go-Lang
  • Dengan menggunakan package, kita bisa merapikan kode program yang kita buat
  • Package sendiri sebenarnya hanya direktori folder di sistem operasi kita

buat file di helper/helper.go

package helper
 
func SayHello(name string) string {
	return "Hello " + name
}

Import

  • Secara standar, file Go-Lang hanya bisa mengakses file Go-Lang lainnya yang berada dalam package yang sama
  • Jika kita ingin mengakses file Go-Lang yang berada diluar package, maka kita bisa menggunakan Import

file: import.go

package main
 
import (
	"dasar/helper"
	"fmt"
)
 
func main() {
	fmt.Println(helper.SayHello("Rahmat"))
 
}

Access Modifier

  • Di bahasa pemrograman lain, biasanya ada kata kunci yang bisa digunakan untuk menentukan access modifier terhadap suatu function atau variable
  • Di Go-Lang, untuk menentukan access modifier, cukup dengan nama function atau variable
  • Jika nama nya diawali dengan hurup besar, maka artinya bisa diakses dari package lain, jika dimulai dengan hurup kecil, artinya tidak bisa diakses dari package lain. Contohnya pada Package

file: helper/helper.go

package helper
 
// tidak bisa diakses diluar
var varsion = "1.0.0"
 
// bisa diakses diluar
func SayHello(name string) string {
	return "Hello " + name
}

file: import.go

package main
 
import (
	"dasar/helper"
	"fmt"
)
 
func main() {
	fmt.Println(helper.SayHello("Rahmat"))
 
	fmt.Println(helper.version) // undefined
}

Package Initialization

  • Saat kita membuat package, kita bisa membuat sebuah function yang akan diakses ketika package kita diakses
  • Ini sangat cocok ketika contohnya, jika package kita berisi function-function untuk berkomunikasi dengan database, kita membuat function inisialisasi untuk membuka koneksi ke database
  • Untuk membuat function yang diakses secara otomatis ketika package diakses, kita cukup membuat function dengan nama init

file: database/database.go

package database
 
var connection string
 
func init() {
	connection = "MySQL"
}
 
func GetDatabase() string {
	return connection
}

file: init.go

package main
 
import (
	"dasar/database"
	"fmt"
)
 
func main() {
	fmt.Println(database.GetDatabase()) // MySQL
}

Blank Identifier

  • Kadang kita hanya ingin menjalankan init function di package tanpa harus mengeksekusi salah satu function yang ada di package
  • Secara default, Go-Lang akan komplen ketika ada package yang di import namun tidak digunakan
  • Untuk menangani hal tersebut, kita bisa menggunakan blank identifier _ sebelum nama package ketika melakukan import

file: internal/internal.go

package internal
 
import "fmt"
 
func init() {
	fmt.Println("Internal Jalan")
}

file: init.go

package main
 
import (
	"dasar/database"
	"dasar/internal" // hilang saat save(auto format)
	"fmt"
)
 
func main() {
	fmt.Println(database.GetDatabase())
}
  • Baris "dasar/internal" akan hilang saat di save (auto format) karena package tersebut tidak digunakan

solusinya gunakan blank Identifier gunakan simbol _ pada file init.go:

package main
 
import (
	"dasar/database"
	_ "dasar/internal" // blank identifier
	"fmt"
)
 
func main() {
	fmt.Println(database.GetDatabase())
}
➜ go run init.go 
Internal Jalan
MySQL

error Interface

  • Go-Lang memiliki interface yang digunakan sebagai kontrak untuk membuat error, nama interface nya adalah error

Membuat Error

  • Untuk membuat error, kita tidak perlu manual.
  • Go-Lang sudah menyediakan library untuk membuat helper secara mudah, yang terdapat di package errors
package main
 
import (
	"errors"
	"fmt"
)
 
func Pembagian(nilai int, pembagi int) (int, error) {
	if pembagi == 0 {
		return 0, errors.New("Pembagian dengan NOL")
	} else {
		return nilai / pembagi, nil
	}
}
 
func main() {
 
	hasil, err := Pembagian(10, 0)
	if (err) == nil {
		fmt.Println(hasil)
	} else {
		fmt.Println("Error", err.Error())
	}
}

Membuat Custom Error

  • Karena error adalah sebuah interface, jadi jika kita ingin membuat error sendiri, kita bisa membuat struct yang mengikuti kontrak dari interface error
package main
 
import "fmt"
 
type validationError struct {
	Message string
}
 
func (v *validationError) Error() string {
	return v.Message
}
 
type notFoundError struct {
	Message string
}
 
func (n *notFoundError) Error() string {
	return n.Message
}
 
func SaveData(id string, data any) error {
	if id == "" {
		return &validationError{"validation error"}
	}
 
	if id != "rahmat" {
		return &notFoundError{"data not found"}
	}
 
	// ok
	return nil
}
 
func main() {
	err := SaveData("budi", nil)
	if err != nil {
		// terjadi error
		if validationErr, ok := err.(*validationError); ok {
			fmt.Println("validation error:", validationErr.Error())
		} else if notFoundErr, ok := err.(*notFoundError); ok {
			fmt.Println("not found error:", notFoundErr.Error())
		} else {
			fmt.Println("unknown error:", err.Error())
		}
 
		// switch finalError := err.(type) {
		// case *validationError:
		// 	fmt.Println("validation error:", finalError.Error())
		// case *notFoundError:
		// 	fmt.Println("not found error:", finalError.Error())
		// default:
		// 	fmt.Println("unknown error:", finalError.Error())
		// }
 
	} else {
		// sukses
		fmt.Println("Sukses")
	}
}

Lanjutan