- Bir struct tanımlamak, literal ile oluşturmak ve
%+vile yazdırmak &ve*ile işaretçileri kullanmak- Değer ve işaretçi alıcı arasında doğru seçim yapmak
NewXkurucu fonksiyonları ve struct gömme ile çalışmak
Bir öğrenciyle ilgili bilgiler (ad, yaş, not ortalaması) bir arada durmalıdır. Onları üç ayrı değişkende tutmak zahmetlidir. Go'da sınıf yoktur; onun yerine ilgili alanları tek bir türde toplayan struct'lar ve bu türe davranış ekleyen metotlar vardır. Bu derste ayrıca işaretçileri de öğreneceksin: onlar olmadan bir fonksiyon asıl veriyi değiştiremez.
Struct'lar
Bir struct type Ad struct { ... } biçiminde tanımlanır ve içinde alanlar ile türleri sıralanır. Bir değer oluşturmak için struct literal'i yazılır: Student{Name: "Aysel", Age: 15}. Alanlara nokta ile erişilir: s.Name. Değer verilmeyen alan sıfır değerini alır; bu yüzden boş bir struct bile tamamen kullanılabilir bir değerdir.
package main
import "fmt"
type Student struct {
Name string
Age int
Score float64
}
func main() {
s := Student{Name: "Aysel", Age: 15, Score: 92.5}
var empty Student
s.Age++
fmt.Println(s)
fmt.Printf("%+v\n", s)
fmt.Printf("%+v\n", empty)
fmt.Println(s.Name, s.Score)
}{Aysel 16 92.5}
{Name:Aysel Age:16 Score:92.5}
{Name: Age:0 Score:0}
Aysel 92.5%v (ve Println) yalnızca değerleri gösterir, %+v ise alan adlarını da ekler; hata ayıklarken bu çok kullanışlıdır. Bütün alanları karşılaştırılabilir olan struct'lar == ile karşılaştırılabilir: iki Student değeri tüm alanları eşit olduğunda eşittir.
İşaretçiler
Go'da bir fonksiyona verilen her şey kopyalanır. Fonksiyon parametresini değiştirirse çağıran taraftaki değişken etkilenmez. Asıl değeri değiştirmek için bir işaretçi gerekir: değişkenin bellekteki adresi. &x adresi alır, *p ise o adresteki değeri okur ya da değiştirir. *int türü “bir int'e işaretçi” demektir. Go'da işaretçi aritmetiği (C'deki gibi p++) yoktur; bu yüzden işaretçiler güvenlidir.
package main
import "fmt"
func doubleValue(n int) {
n *= 2
}
func doublePointer(n *int) {
*n *= 2
}
func main() {
x := 5
p := &x
*p = 7
fmt.Println(x, *p, p == &x)
doubleValue(x)
fmt.Println(x)
doublePointer(&x)
fmt.Println(x)
}7 7 true 7 14
Başka bir değişkenin bellekteki adresini tutan değer. Sıfır değeri nil'dir. & adresi alır, * ise o adresteki değere erişir.
Metotlar: değer ve işaretçi alıcılar
Bir metot, alıcı (receiver) adı verilen özel bir parametresi olan fonksiyondur. Alıcı, func sözcüğü ile metodun adı arasına yazılır: func (a Account) Summary() string. Alıcı bir değerse (a Account) metot nesnenin kopyasıyla çalışır. Bir işaretçiyse (a *Account) metot asıl nesneyi değiştirebilir.
package main
import "fmt"
type Account struct {
Owner string
Balance float64
}
func (a Account) Summary() string {
return fmt.Sprintf("%s: %.2f AZN", a.Owner, a.Balance)
}
func (a *Account) Deposit(amount float64) {
a.Balance += amount
}
func (a Account) BrokenDeposit(amount float64) {
a.Balance += amount
}
func main() {
acc := Account{Owner: "Murad", Balance: 100}
acc.Deposit(50)
acc.BrokenDeposit(1000)
fmt.Println(acc.Summary())
p := &acc
p.Deposit(25.5)
fmt.Println(p.Summary())
}Murad: 150.00 AZN Murad: 175.50 AZN
BrokenDeposit 1000 manat eklemeye çalıştı ama bakiye değişmedi, çünkü metot bir kopya üzerinde çalışıyordu. Dikkat: acc bir işaretçi değil, yine de acc.Deposit(50) çalışıyor; Go otomatik olarak (&acc).Deposit(50) çağırır. Aynı şekilde p işaretçisi üzerinden değer alıcılı Summary metodu da çağrılabilir.
| Alıcı | Ne zaman seçilmeli |
|---|---|
func (a Account) | Metot nesneyi değiştirmiyor ve tür küçük (birkaç alan, time.Time gibi). |
func (a *Account) | Metot alanları değiştiriyor, tür büyük ya da içinde sync.Mutex gibi kopyalanmaması gereken bir alan var. |
type Counter struct {
value int
}
func (c Counter) Increment() {
c.value++ // changes a copy
}type Counter struct {
value int
}
func (c *Counter) Increment() {
c.value++ // changes the original
}Kurucular ve gömme
Go'da özel kurucular (constructor) yoktur. Onların yerine adı New ile başlayan sıradan bir fonksiyon yazılır: NewTeacher(...) bir nesne oluşturur ve ona bir işaretçi döndürür. Kalıtım da yoktur; Go bileşimi (composition) tercih eder. Bir struct başka bir struct'ın içine adsız bir alan olarak gömülebilir (embedding): o zaman gömülen struct'ın alanları ve metotları dıştaki struct üzerinden doğrudan kullanılabilir.
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) Greet() string {
return "Hi, I am " + p.Name
}
type Teacher struct {
Person
Subject string
}
func NewTeacher(name, subject string) *Teacher {
return &Teacher{Person: Person{Name: name}, Subject: subject}
}
func main() {
t := NewTeacher("Leyla", "Math")
fmt.Println(t.Greet())
fmt.Println(t.Name, "teaches", t.Subject)
fmt.Printf("%+v\n", *t)
}Hi, I am Leyla
Leyla teaches Math
{Person:{Name:Leyla} Subject:Math}Önemli noktalar
- Bir struct ilgili alanları tek bir türde toplar;
%+vonu alan adlarıyla yazdırır. - Go her şeyi değerle aktarır; asıl değeri değiştirmek için bir işaretçi (
&x,*p) gerekir. - Değer alıcı bir kopyayla, işaretçi alıcı asıl nesneyle çalışır;
&işaretini Go kendisi ekler. nilbir işaretçi üzerinden alana erişmek panic'e yol açar.- Kurucular yerine
NewXfonksiyonları, kalıtım yerine struct gömme (bileşim) kullanılır.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
fmt.Printf("%+v", Point{X: 1, Y: 2}) ne yazar?