Menu

Golang Metotlar: Değer ve Pointer Alıcılar

Metot, alıcısı (receiver) olan bir fonksiyondur. Metot tanımlamayı, değer ya da pointer alıcının ne zaman kullanılacağını, metot kümelerini ve interface'leri, ve struct olmayan tiplerdeki metotları öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Metot tanımlamak

Metot, alıcısı olan bir fonksiyondur: metot adından önce yazılan ek bir parametre. Alıcı metodu bir tipe bağlar.

Alıcı adı genellikle tipten alınan bir ya da iki harftir (Circle için c). Go this ya da self kullanmaz ve stil rehberleri bu adları önermez.

Circle'ın bir String() string metodu olduğu için fmt.Stringer'ı karşılar ve fmt.Println, {2} yerine sonucu yazdırır.

Değer alıcılar ve pointer alıcılar

Bir değer alıcı (c Circle) bir kopya alır. Bir pointer alıcı (c *Circle) adresi alır, bu yüzden alanlardaki değişiklikler kalıcı olur.

IncByValue derlenir ve görünür hiçbir şey yapmaz; klasik bir hata. Bir metodun alıcısını değiştirmesi amaçlanıyorsa pointer alıcıya ihtiyacı vardır.

Çağrılar Go'nun sağladığı iki kolaylığı da gösteriyor. Adreslenebilir bir değişken üzerinde c.Inc(), (&c).Inc() olarak yeniden yazılır; bir pointer üzerinde p.Value() ise (*p).Value() olarak yeniden yazılır. Yalnızca bir metodu çağırmak için nadiren & ya da * yazarsınız.

Otomatik & adreslenebilir bir şey ister. Bir map elemanı ya da bir bileşik literal adreslenebilir değildir:

m := map[string]Counter{"a": {}}
m["a"].Inc()    // compile error: cannot call pointer method Inc on Counter
Counter{}.Inc() // same error: a composite literal is not addressable

Map'te pointer saklayın (map[string]*Counter) ya da değeri dışarı kopyalayın, değiştirin ve geri yazın.

Hangi alıcıyı seçmeli

Pointer alıcı kullanın, eğerDeğer alıcı kullanın, eğer
Metot alıcıyı değiştiriyorsaTip küçükse ve metotları onu hiç değiştirmiyorsa (time.Time, bir nokta)
Struct büyükse (kopyalamak maliyetliyse)Tip bir map, func ya da chan ise (zaten bir referans)
Tip bir sync.Mutex ya da benzeri tutuyorsaDeğerin, kopyalaması güvenli ilkel bir tip gibi davranmasını istiyorsanız
Tipteki diğer metotlar zaten pointer kullanıyorsa

Go SSS'si ve standart kütüphane tek bir pratik kurala uyar: tutarlı olun. Herhangi bir metot pointer alıcıya ihtiyaç duyuyorsa, metot kümesi öngörülebilir olsun diye tüm metotlara pointer alıcı verin. go vet, bir değer alıcı üzerinden kopyalanan ve sync.Mutex tutan bir struct'ı işaretler (passes lock by value).

Metot kümeleri ve interface'ler

Bu ayrım en çok interface'ler için önemlidir. T'nin metot kümesi değer alıcılı metotları içerir. *T'nin metot kümesi hem değer hem de pointer alıcılı metotları içerir.

O slice'ta & olmadan Square{Side: 3} yazmak derlenmez:

cannot use Square{…} (value of struct type Square) as Shape value in array or slice literal: Square does not implement Shape (method Area has pointer receiver)

Nedeni: bir interface, adreslenebilir olmayan bir Square kopyası tutabilir, bu yüzden Go bir pointer metodunu çağırmak için onun adresini alamaz. Hikâyenin geri kalanı için interface'ler sayfasına bakın.

Struct olmayan tiplerde metotlar

Yalnızca struct'lar değil, paketinizde tanımlanan her adlandırılmış tipin metotları olabilir.

Sınırlar: tip metotla aynı pakette tanımlanmış olmalıdır ve temel tipi bir pointer ya da interface olamaz. Kendi paketinizden int, string ya da time.Time'a metot ekleyemezsiniz. Go'daki yol, yeni bir adlandırılmış tipe sarmaktır. Bir tam sayı tipi üzerindeki String(), enum'ların okunabilir çıktı almasının da yoludur; bkz. iota ile enum'lar.

Metot değerleri ve metot ifadeleri

Belirli bir alıcıya bağlanmış bir metot bir fonksiyon değeridir:

Değer alıcılı bir metot değeri, oluşturulduğu anda alıcıyı kopyalar; f'nin hâlâ Ana demesinin nedeni budur. Pointer alıcıyla pointer'ı saklar ve sonraki değişiklikleri görürdü. Metot değerleri callback'ler için kullanışlıdır: http.HandleFunc("/", srv.handleIndex).

Sık yapılan hatalar

  • Değiştiren bir metotta değer alıcı. Derlenir, sessizce bir kopyayı değiştirir.
  • Alıcı türlerini karıştırmak. Bir interface'i T'nin mi yoksa *T'nin mi karşıladığını belirsiz yapar. Her tip için birini seçin.
  • Nil bir pointer üzerinde pointer metodu çağırmak. İzinlidir ve metot nil bir alıcıyla çalışır. Yalnızca bir alana dokunduğunda panic olur. Bazı tipler (nil güvenli bir bağlı liste gibi) bunu bilerek kullanır; çoğu ise beklemez.

Sıkça Sorulan Sorular

Go'da metot ile fonksiyon arasındaki fark nedir?

Bir metodun func ile ad arasına yazılan bir alıcısı vardır: func (c Circle) Area() float64. Onu bir değer üzerinde çağırırsınız (c.Area()), tipin metot kümesine aittir ve bir tipin interface'leri karşılama yolu budur. Bunun dışında ilk argümanı alıcı olan bir fonksiyon gibi davranır.

Go'da pointer alıcı ne zaman kullanılmalı?

Metodun alıcıyı değiştirmesi gerektiğinde, struct her çağrıda kopyalanması israf olacak kadar büyük olduğunda ya da tip sync.Mutex gibi kopyalanmaması gereken bir şey içerdiğinde pointer alıcı kullanın. Bir tipin herhangi bir metodu pointer alıcıya ihtiyaç duyuyorsa, olağan tavsiye tutarlılık için hepsini pointer alıcı yapmaktır.

Metodun pointer alıcısı olduğunda tipim interface'i neden gerçekleştirmiyor?

Pointer alıcılı metotlar T'nin değil, *T'nin metot kümesine aittir. Bu yüzden var s Shape = Square{} ifadesi Square does not implement Shape (method Area has pointer receiver) hatasıyla başarısız olur. Bunun yerine bir pointer atayın: var s Shape = &Square{}.

Go'da yerleşik tiplere metot tanımlanabilir mi?

Doğrudan değil. Metotlar yalnızca aynı pakette tanımlanmış adlandırılmış tiplere tanımlanabilir. Önce kendi tipinizi tanımlayın, type Celsius float64, sonra ona metotlar ekleyin. int, string ya da başka bir paketten bir tipe metot ekleyemezsiniz.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA