Setiap package yang kita tulis memuat dua jenis kode sekaligus: bagian yang memang disediakan untuk pemanggil luar, dan bagian dalam yang sengaja disembunyikan agar bebas diubah tanpa mengganggu siapa pun. Bahasa berorientasi objek klasik mengaturnya lewat kata kunci public, private, dan protected. Go memilih mekanisme yang jauh lebih sederhana: kapitalisasi huruf pertama identifier. Satu aturan kecil ini berlaku seragam untuk fungsi, tipe, method, field struct, konstanta, hingga variabel — dan pada chapter ini kita akan mengujinya lintas package dengan galat kompilasi sungguhan.

Aturan Kapitalisasi #

Identifier yang diawali huruf kapital (huruf besar Unicode) bersifat exported: dapat diakses oleh package lain melalui import. Identifier yang diawali huruf kecil bersifat unexported: hanya dapat diakses oleh kode di dalam package yang sama. Tabel berikut memperlihatkan cakupan aturan ini:

IdentifierPenulisanVisibilitas
FungsiBaris / rapikanKapital: lintas package; kecil: dalam package
Tipe dataPengguna / penggunaKecilSama
Field structNama / emailSama
MethodKontak / koreksiSama
Konstanta & variabelSalam / penutupSama

Aturan ini dievaluasi per identifier, bukan per berkas. Dua berkas di direktori yang sama yang sama-sama menuliskan package teks dapat saling memanggil identifier kecil milik satu sama lain — sebab keduanya adalah satu package.

Demo: Package teks dari Luar #

Bundle chapter ini berisi module contoh.com/produk dengan package teks yang memadukan identifier exported dan unexported:

go
package teks

import "strings"

// Salam adalah sapaan baku yang boleh dipakai pemanggil luar.
const Salam = "Halo"

// penutup hanya dipakai di dalam package teks.
const penutup = "!"

// Pengguna menyimpan identitas pemakai aplikasi.
type Pengguna struct {
	Nama  string // diekspor: boleh dibaca pemanggil
	email string // privat: hanya bisa diakses dari package teks
}

// Baru membuat Pengguna dengan nama dan email lengkap.
func Baru(nama, email string) Pengguna {
	return Pengguna{Nama: nama, email: email}
}

// Kontak mengembalikan alamat kontak dalam format tampilan.
func (p Pengguna) Kontak() string {
	return p.Nama + " <" + p.email + ">"
}

// Baris mengubah teks menjadi satu baris rapi diakhiri penutup.
func Baris(t string) string {
	return rapikan(t) + penutup
}

// rapikan memangkas spasi berlebih; tidak untuk pemanggil luar.
func rapikan(t string) string {
	return strings.TrimSpace(t)
}

Sementara src/main.go hanya boleh menyentuh identifier kapital:

go
package main

import (
	"fmt"

	"contoh.com/produk/teks"
)

func main() {
	budi := teks.Baru("Budi", "[email protected]")
	fmt.Println(teks.Salam, budi.Nama, budi.Kontak())
	fmt.Println(teks.Baris("  pesan ber-spasi  "))
}

Jalankan dari dalam direktori src/:

bash
cd src && go run .

Output yang Dihasilkan:

text
Halo Budi Budi <[email protected]>
pesan ber-spasi!

Mari kita telaah baris demi baris kedua berkas:

  1. teks.go baris 6 (const Salam): konstanta kapital — pemanggil luar boleh membaca teks.Salam.
  2. teks.go baris 9 (const penutup): konstanta kecil — hanya terlihat di dalam teks, termasuk oleh fungsi Baris.
  3. teks.go baris 12-16 (type Pengguna): field Nama diekspor, sedangkan field email tidak; struktur ini membuat pembentukan nilai dari luar harus melewati fungsi Baru.
  4. teks.go baris 18-21 (func Baru): konstruktor exported yang menjadi satu-satunya pintu mengisi field email dari luar — pola enkapsulasi khas Go.
  5. teks.go baris 32-35 (func rapikan): fungsi kecil sebagai detail internal; keberadaannya boleh dihapus tanpa memengaruhi API publik package.
  6. main.go baris 10-12: seluruh akses memakai identifier kapital (Baru, Salam, Nama, Kontak, Baris) — bila satu saja diganti identifier kecil, program gagal dikompilasi.

Galat Nyata Saat Melanggar Batas #

Ketika pemanggil luar mencoba menyentuh identifier kecil, compiler menolak dengan pesan yang spesifik. Ketiga cuplikan berikut dijalankan nyata pada salinan module demo (identifier unexported disengaja):

go
budi := teks.Pengguna{Nama: "Budi", email: "[email protected]"}
text
./main.go:10:38: cannot refer to unexported field email in struct literal of type teks.Pengguna
go
fmt.Println(budi.email)
text
./main.go:11:19: budi.email undefined (cannot refer to unexported field email)
go
fmt.Println(teks.rapikan("  tes  "))
fmt.Println(teks.penutup)
text
./main.go:10:19: name rapikan not exported by package teks
./main.go:11:19: undefined: teks.penutup

Perhatikan corak pesannya: pelanggaran berupa nama fungsi menghasilkan name ... not exported by package, sedangkan nama konstanta yang tidak dikenal compiler di package lain dilaporkan sebagai undefined. Semuanya tertangkap sebelum program berjalan — visibilitas Go ditegakkan saat kompilasi, bukan runtime.

Tanpa protected, Batasnya Package #

Go tidak mengenal protected atau friend. Tidak ada tingkat “terlihat oleh subclass” karena pewarisan tidak menjadi mekanisme utama Go. Batas visibilitas hanya satu: package. Kode pada direktori lain yang berada dalam module yang sama pun tetap dianggap pihak luar — sehingga aturan “package = direktori” yang kita pelajari sebelumnya sekaligus menjadi batas privasi kode.

Sebuah package yang dirakit rapi — konstanta publik secukupnya, konstruktor jelas, detail internal disembunyikan — menjadi dasar bagi sesuatu yang sering diabaikan pemula: urutan inisialisasi. Ketika package Anda diimpor, variabel dan fungsi init() di dalamnya dijalankan lebih dulu daripada kode pemanggil — perilaku yang kita bedah tuntas pada chapter Package Initialization .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

src/main.go

Unduh berkas
go
package main

import (
	"fmt"

	"contoh.com/produk/teks"
)

func main() {
	budi := teks.Baru("Budi", "[email protected]")
	fmt.Println(teks.Salam, budi.Nama, budi.Kontak())
	fmt.Println(teks.Baris("  pesan ber-spasi  "))
}

src/teks/teks.go

Unduh berkas
go
package teks

import "strings"

// Salam adalah sapaan baku yang boleh dipakai pemanggil luar.
const Salam = "Halo"

// penutup hanya dipakai di dalam package teks.
const penutup = "!"

// Pengguna menyimpan identitas pemakai aplikasi.
type Pengguna struct {
	Nama  string // diekspor: boleh dibaca pemanggil
	email string // privat: hanya bisa diakses dari package teks
}

// Baru membuat Pengguna dengan nama dan email lengkap.
func Baru(nama, email string) Pengguna {
	return Pengguna{Nama: nama, email: email}
}

// Kontak mengembalikan alamat kontak dalam format tampilan.
func (p Pengguna) Kontak() string {
	return p.Nama + " <" + p.email + ">"
}

// Baris mengubah teks menjadi satu baris rapi diakhiri penutup.
func Baris(t string) string {
	return rapikan(t) + penutup
}

// rapikan memangkas spasi berlebih; tidak untuk pemanggil luar.
func rapikan(t string) string {
	return strings.TrimSpace(t)
}