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.
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 Data
Nilai Minimum
Nilai Maksimum
int8
-128
127
int16
-32768
32767
int32
-2147483648
2147483647
int64
-9223372036854775808
9223372036854775807
Gunakan tipe data number yang sesuai dengan kebutuhan, jika semua menggunakan yang paling besar nanti ukuran yang dibutuhkan memori juga ikut besar.
Tipe Data
Nilai Minimum
Nilai Maksimum
uint8
0
255
uint16
0
65535
uint32
0
4294967295
uint64
0
18446744073709551615
uint atau unsigned integer adalah tipe data yang tidak memiliki angka negatif.
Tipe Data
Nilai Minimum
Nilai Maksimum
float32
1.18×10−38
3.4×1038
float64
2.23×10−308
1.80×10308
complex64
complex numbers with float32 real and imaginary parts.
complex128
complex 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 Data
Alias untuk
byte
uint8
rune
int32
int
Minimal int32
uint
Minimal uint32
Tipe data alias adalah nama lain dari tipe data number yang ada contohnya seperti diatas.
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 mainimport "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 mainimport "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
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 mainimport "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 3276832768-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 mainimport "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 mainimport "fmt"func main() { type noKTP string var KTP1 noKTP = "111111111111" fmt.Println(KTP1) // menggunakan type noKTP fmt.Println(noKTP("222222222222"))}
Operasi Matematika
Operator
Keterangan
+
Penjumlahan
-
Pengurangan
*
Perkalian
/
Pembagian
%
Sisa Pembagian
package mainimport "fmt"func main() { var a = 10 var b = 30 var c = a + b fmt.Println(c) // 40}
Augmented Assignments
Operasi Matematika
Augmented Assignments
a = a + 10
a += 10
a = a - 10
a -= 10
a = a * 10
a *= 10
a = a / 10
a /= 10
a = a % 10
a %= 10
package mainimport "fmt"func main() { var a = 10 a += 20 fmt.Println(a) // 30}
Unary Operator
Operator
Keterangan
++
a = a + 1
—
a = a - 1
-
Negative
+
Positive
!
Boolean kebalikan
package mainimport "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
Operator
Keterangan
>
Lebih Dari
<
Kurang Dari
>=
Lebih Dari Sama Dengan
⇐
Kurang Dari Sama Dengan
==
Sama Dengan
!=
Tidak Sama Dengan
package mainimport "fmt"func main() { var a = 20 var b = 10 fmt.Println(a > b) // true}
Operasi Boolean
Operator
Keterangan
&&
Dan
|
Atau
!
Kebalikan
Operasi &&
Nilai 1
Operator
Nilai 2
Hasil
true
&&
true
true
true
&&
false
false
false
&&
true
false
false
&&
false
false
Operasi ||
Nilai 1
Operator
Nilai 2
Hasil
true
|
true
true
true
|
false
true
false
|
true
true
false
|
false
false
Operasi !
Operator
Nilai 2
Hasil
!
true
false
!
false
true
package mainimport "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
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
Operasi
Keterangan
len(array)
Untuk mendapatkan panjang Array
array[index]
Mendapat data di posisi index
array[index] = value
Mengubah data di posisi index
Array tidak memiliki fungsi untuk menghapus element
package mainimport "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 mainimport "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 Slice
Keterangan
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
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 mainimport "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)}
Untuk meng-copy slice dari fromSlice yang pertama dilakukan menyiapakn wadah atau variable slice baru
gunakan function make (tipe element, panjang, dan kapasitas)
gunakan function copy
argument yang pertama adalah slice baru atau destinasi tujuan toSlice
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.
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 mainimport "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
Operasi
Keterangan
len(map)
Untuk mendapatkan jumlah data di map
map[key]
Mengambil data di map dengan key
map[key] = value
Mengubah data di map dengan key
make(map[TypeKey]TypeValue)
Membuat map baru
delete(map, key)
Menghapus data di map dengan key
package mainimport "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 mainimport "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 mainimport "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 mainimport "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 mainimport "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 mainimport "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 mainimport "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 mainimport "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") }}
Untuk kasus diatas lebih baik menggunakan If Expression
For
Dalam bahasa pemrograman, biasanya ada fitur yang bernama perulangan
Salah satu fitur perulangan adalah for loops
package mainimport "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 mainimport "fmt"func main() { for counter := 1; counter <= 10; counter++ { fmt.Println("Perulangan ke ", counter) }}
package mainimport "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 mainimport "fmt"func main() { names := []string{"rahmat", "ardiansyah", "eko", "kurniawan"} for index, name := range names { fmt.Println("index ke -", index, " = ", name) }}
package mainimport "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 mainimport "fmt"func main() { for i := 0; i < 10; i++ { if i == 5 { break } fmt.Println(i) }}
package mainimport "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
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
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 mainimport "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
➜ go run panic.go // falseAplikasi BerjalanAplikasi Berhentilearn/go/dasar via 🐹 v1.25.4 ➜ go run panic.go // true Aplikasi Berhentipanic: uppsgoroutine 1 [running]:main.runApplication(0x80?) /home/rahmat/Documents/learn/go/dasar/panic.go:13 +0x8emain.main() /home/rahmat/Documents/learn/go/dasar/panic.go:20 +0x18exit 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
➜ go run panic.go Aplikasi Berhentipanic: uppsgoroutine 1 [running]:main.runApplication(0x80?) /home/rahmat/Documents/learn/go/dasar/panic.go:13 +0x8emain.main() /home/rahmat/Documents/learn/go/dasar/panic.go:20 +0x18exit 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 mainimport "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
➜ go run struct.go{Joko Indonesia 30}{Budi Indonesia 27}Halo Rahmat nama saya JokoHalo 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
Keunggulan
Penjelasan
Fleksibel
Tipe mana pun bisa memenuhi interface cukup dengan memiliki method yang sesuai.
Modular
Implementasi bisa diganti tanpa mengubah kode yang memakai interface.
Mudah diuji
Cocok untuk mocking ketika unit test.
Mendukung Polimorfisme
Fungsi 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
➜ 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 mainimport "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 mainimport "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 mainimport "fmt"// Membuat interfacetype Speaker interface { Speak() string}// Struct pertamatype Dog struct{}func (d Dog) Speak() string { return "Guk Guk"}// Struct keduatype Cat struct{}func (c Cat) Speak() string { return "Meow"}// Fungsi yang menerima interfacefunc MakeSound(s Speaker) { fmt.Println(s.Speak())}func main() { dog := Dog{} cat := Cat{} MakeSound(dog) MakeSound(cat)}
➜ go run interface.go Guk GukMeow
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 mainimport "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
➜ 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 mainimport "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 mainimport "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 mainimport "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 mainimport "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 mainimport "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 address1masih ikut berubah karena coding address2.City = "Bandung" masih mengarah ke reference address1
Pointer
Tanda &harus tetap digunakan karena sebelumnya sudah menggunakan type pointer,
package mainimport "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 mainimport "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 mainimport "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 mainimport "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:
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 diluarvar varsion = "1.0.0"// bisa diakses diluarfunc SayHello(name string) string { return "Hello " + name}
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