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.
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
GOPATH
GOPATH adalah variabel yang digunakan oleh Go sebagai rujukan lokasi di mana semua folder project disimpan (kecuali untuk yg diinisialisasi menggunakan Go Modules). GOPATH berisikan 3 buah sub-folder: src, bin, dan pkg.
Command
Command go mod init
Command go mod init digunakan untuk inisialisasi project pada Go yang menggunakan Go Modules. Untuk nama project bisa menggunakan apapun, tapi umumnya disamakan dengan nama direktori/folder.
$ mkdir <nama-project>$ cd <nama-project>$ go mod init <nama-project>
Command go run
Command go run digunakan untuk eksekusi file program, yaitu file yang ber-ekstensi .go. Cara penggunaannya dengan menuliskan command tersebut diikuti argumen nama file.
$ go run main.go
Command go test
Go menyediakan package testing, berguna untuk keperluan pembuatan file test. Pada penerapannya, ada aturan yang wajib diikuti yaitu nama file test harus berakhiran _test.go.
$ go test main_test.go
Command go build
Command ini digunakan untuk mengkompilasi file program.
Sebenarnya ketika eksekusi program menggunakan go run didalamnya terjadi proses kompilasi juga. File hasil kompilasi kemudian disimpan pada folder temporary untuk selanjutnya langsung dieksekusi.
Berbeda dengan go build, command ini menghasilkan file executable atau binary pada folder yang sedang aktif. Contoh praktiknya bisa dilihat di bawah ini.
Command go get
Command go get digunakan untuk men-download package atau dependency. Sebagai contoh, penulis ingin men-download package Kafka driver untuk Go pada project project-pertama, maka command-nya kurang lebih seperti berikut:
$ cd project-pertama$ go get github.com/segmentio/kafka-go
Pada contoh di atas, bisa dilihat bahwa URL github.com/segmentio/kafka-go merupakan URL package kafka-go. Package yang sudah terunduh tersimpan dalam temporary folder yang ter-link dengan project folder di mana command go get dieksekusi, menjadikan project tersebut bisa meng-import package yang telah di-download.
Untuk mengunduh package/dependency versi terbaru, gunakan flag -u pada command go get, contohnya:
$ go get -u github.com/segmentio/kafka-go
Command go get harus dijalankan dalam folder project. Jika dijalankan di-luar path project maka dependency yang ter-unduh akan ter-link dengan GOPATH, bukan dengan project.
Command go mod download
Command go mod download digunakan untuk men-download dependency.
Command go mod tidy
Command go mod tidy digunakan untuk memvalidasi dependency sekaligus men-download-nya jika memang belum ter-download.
Command go mod vendor
Command ini digunakan untuk vendoring.
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
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 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 adalah sebuah tipe data yang digunakan untuk menggabungkan nol atau lebih tipe data lainnya dalam satu kesatuan
struct pada dasarnya kumpulan tipe data yang digabungkan dalam satu field
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 Pascal Case, artinya jika diawali dengan huruf kapital akan membuat struct menjadi public dimana jika public bisa diakses di file kode lain. Jika struct diawali dengan huruf kecil maka akan menjadi private dimana hanya dapat digunakan di file kode saat ini.
struct sangat berguna untuk mendapatkan salah satu atau lebih nilai daripada harus menggunakan array dan meng-iterasinya jika ingin mendapatkan salah satu datanya
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{Rahmat Ardiansyah Indonesia 0}0
Anonymouse struct
struct juga bisa dibuat tanpa namanya, yang disebut dengan anonymouse struct
tujuan menggunakan anonymouse struct adalah salah satu solusi tercepat untuk mendapatkan tipe data yang sekali pakai (tidak reusable) artinya hanya di dalam function saat ini saja.
➜ go run struct.go{Joko Indonesia 30}{Budi Indonesia 27}Halo Rahmat nama saya JokoHalo Rahmat nama saya Budi
Nested struct
Nested struct biasanya disebut composition
artinya struct bisa di dalam struct lainnya
composiiton tidak sama dengan inheritance
package mainimport "fmt"type Employee struct { name string age int isRemote bool Address}type Address struct { street string city string}func (e *Employee) updateName(newName string) { e.name = newName}func main() { address1 := Address{ street: "Jln sepakat", city: "pekanbaru", } employee1 := Employee{ name: "Rahmat", age: 24, isRemote: true, Address: address1, } employee1.updateName("Rahmat Ardiansyah") fmt.Println(employee1.street)}
➜ go run anonymouse_struct.goLocal Employee Rahmat ArdiansyahJln sepakat
Untuk meng-akses address gunakan struct employee langsung, tanpa harus meng-aksesnya menggunakan struct Address
package mainimport "fmt"type Employee struct { name string age int isRemote bool Address}type Address struct { street string city string}func (a Address) printCity() { fmt.Println(a.city)}func (e *Employee) updateName(newName string) { e.name = newName}func main() { address1 := Address{ street: "Jln sepakat", city: "Pekanbaru", } employee1 := Employee{ name: "Rahmat", age: 24, isRemote: true, Address: address1, } employee1.updateName("Rahmat Ardiansyah") fmt.Println(employee1.street) employee1.printCity()}
➜ go run anonymouse_struct.goJln sepakatPekanbaru
nested struct juga bisa langsung dibuat methodnya dan bisa langsung di akses menggunakan struct (*parentnya)
struct tags
Fungsi utama struct tags adalah sebagai metadata opsional untuk field struct yang memberikan informasi tambahan, terutama untuk keperluan serialisasi/deserialisasi data seperti JSON, interaksi dengan basis data (database), dan validasi.
➜ go run anonymouse_struct.goLocal Employee Rahmat ArdiansyahJln sepakatPekanbaru{"name":"Rahmat Ardiansyah","age":24,"remote":true,"street":"Jln sepakat","city":"Pekanbaru"}
jsonData harus dibuah dulu dari byte ke string.
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
Pointer digunakan untuk alikasi memori atau mudahnya menghemat penggunaan memori
Pass by Value & Pass by Reference
Secara default di Go-Lang semua variable itu di passing by value, bukan pass 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 (pass by reference)
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" // mengubah address2 fmt.Println(address1) // 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 *
jika kita ingin membuat variabel baru dengan nilai yang sama dengan variabel pointer sebelumnya, kita tidak bisa langsung menampilkannya/menggunakannya seperti biasa, contohnya:
package mainimport "fmt"func main() { i, j := 14, 2012 fmt.Println(&i, &j) k := &i fmt.Println(k)}
➜ go run pointer2.go0xc000012120 0xc0000121280xc000012120 (ini adalah p)
Untuk menampilkan atau mendapatkan nilainya gunakan operator *
package mainimport "fmt"func main() { i, j := 14, 2012 fmt.Println(&i, &j) k := &i fmt.Println(*k)}
➜ go run pointer2.go0xc000012120 0xc00001212814
operator * digunakan dengan 2 cara:
*k artinya variable p akan mengembalikan value dari petunjuknya
*int artinya variable ini akan menjadi pointer dengan dengan tipe data int sebagai basisnya
Bagaimana jika kita ingin mengubah value dari variable i tapi menggunakan variable k ? tidak
package mainimport "fmt"func main() { i, j := 14, 2012 fmt.Println(&i, &j) k := &i fmt.Println(*k) k = 10 // error fmt.Println(k)}
Kode diatas akan error karena karena variabel tersebut merupakan sebuah pointer int bukan int
package mainimport "fmt"func main() { i, j := 14, 2012 fmt.Println(&i, &j) var k *int = &i fmt.Println(*k) k = 10 // error fmt.Println(k)}
➜ go run pointer2.go# command-line-arguments./pointer2.go:12:6: cannot use 10 (untyped int constant) as *int value in assignment
Solusinya gunakan operator * untuk membuat membuat assignment baru sebagai tipe datanya
package mainimport "fmt"func main() { i, j := 14, 2012 fmt.Println(&i, &j) var k *int = &i fmt.Println(*k) *k = 10 fmt.Println(i) fmt.Println(*k, k)}
➜ go run pointer2.go0xc00009a020 0xc00009a028141010 0xc00009a020
sekarang variabel i dan k memiliki value yang sama.
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 dibuat tapi bukan pointer:
package mainimport "fmt"func squareVal(v int) { v *= v fmt.Println("di function squareVal", v)}func double(v *int) { *v = *v + *v fmt.Println("Di function squareAdd", v, *v)}func main() { a := 4 squareVal(a) fmt.Println("Varibel A", a) double(&a) fmt.Println("Varibel A", a)}
➜ go run pointer2.godi function squareVal 16Varibel A 4Di function squareAdd 0xc000012108 8Varibel A 8
function squareVal tidak menggunakan pointer, artinya variable a pada main tidak berubah
sedangkan function double menggunakan tipe data pointer sebagai parameter artinya kita harus mengirimkan argument yang merupakan address dari variable a menggunakan operator &
Jika menggunakan pointer maka variable a valuenya juga ikut berubah
Pointer pada return value
package mainimport "fmt"type Person struct { name string age int}func initPerson() Person { person1 := Person{ name: "Rahmat", age: 24, } return person1}func main() { fmt.Println(initPerson()) // akan meng-copy value ke main}
Jika kita memanggil function initPerson pada function main, maka return value pada initPerson akan dicopy dulu ke main artinya menjadi tidak bermutasi (tidak efisien)
Kita bisa menggunakan pointer sebagai return valuenya, contohnya:
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