Tek örnekte gömme
Tipi olan ama adı olmayan bir alan gömülü bir alandır. Gömülü tipin alanları ve metotları doğrudan dış tip üzerinden erişilebilir hale gelir.
Çıktı:
Ana
Hi, I'm Ana
ana@example.com
{User:{Name:Ana Email:ana@example.com} Level:2}
Gömülü alanın adı tip adıdır, User. Literal'lerde (User: User{...}) ve iç değerin kendisine ihtiyaç duyduğunuzda (a.User) ona böyle başvurursunuz. Yükseltilen bir alan bir literal'de kısa adıyla ayarlanamaz: Admin{Name: "Ana"}, unknown field Name in struct literal of type Admin hatasıyla başarısız olur.
Yükseltilen metotlar interface'leri karşılar
Yükseltme, gömülü tipin metotlarını dış tipin metot kümesine ekler. Böylece dış tip, iç tipin karşıladığı her interface'i karşılar.
Metot kümesi kuralları gömülü tipi izler. T'yi gömmek, T'nin değer alıcılı metotlarını hem dış değere hem dış pointer'a, *T'nin pointer alıcılı metotlarını ise yalnızca dış pointer'a yükseltir. *T'yi gömmek iki kümeyi de ikisine yükseltir. Emin değilseniz pointer'ı gömün ya da dış tipi bir pointer üzerinden kullanın.
Bir pointer gömmenin bir bedeli vardır: pointer nil olabilir. Logger'ı olmayan Service{} derlenir ve svc.Log(...) sonra panic olur.
Gömme kalıtım değildir
Gömme alt sınıflamaya benzer, ama üç şey Java ya da Python'dan farklı davranır.
Dış tip iç tip değildir. Bir Admin bir User değildir. User alan bir fonksiyon bir Admin kabul etmez; a.User aktarın.
Sanal dağıtım (virtual dispatch) yoktur. Yükseltilen bir metot gömülü değer üzerinde çalışır ve dış tip hakkında hiçbir şey bilmez. Başka bir metot çağırırsa, dış tip aynı adda bir metot tanımlasa bile kendi tipinin sürümünü çağırır.
Çıktı:
woof
The animal says ...
Sınıf tabanlı bir dilde Speak "woof" yazdırırdı. Go'da d.Speak(), d.Animal.Speak()'in kısaltmasıdır ve o çağrının alıcısı Animal'dır. Tipe göre değişen bir davranış istediğinizde bir interface kullanın: ihtiyaç duyan koda bir Sounder aktarın.
İç tip gömüldüğünü bilmez. super yoktur. Yükseltilen bir metodu genişletmek için dış tipte aynı adda bir metot tanımlayın ve içtekini açıkça çağırın:
func (a Admin) Greet() string {
return a.User.Greet() + " (admin)"
}
Ad çakışmaları ve gölgeleme
Dış tipteki bir alan ya da metot, yukarıdaki Dog.Sound örneğinde olduğu gibi, aynı adlı yükseltilmiş olanı gölgeler. Daha sığ derinlikteki ad kazanır.
Aynı derinlikteki iki gömülü tip aynı adı yükselttiğinde o ad belirsiz hale gelir. Hiçbir şey belirsiz adı kullanmadığı sürece program yine derlenir:
Çakışmayı tam yolu kullanarak ya da ID'yi dış tipte tanımlayarak çözün.
Interface gömmek
Interface'ler başka interface'leri gömebilir. Standart kütüphane daha büyük interface'leri böyle kurar:
type ReadWriter interface {
Reader
Writer
}
Bir struct da bir interface gömebilir. Struct o zaman o interface'i alanda sakladığınız değer üzerinden karşılar ve yalnızca ilgilendiğiniz metotları geçersiz kılarsınız. Bu, neredeyse hiç kod yazmadan decorator kalıbıdır:
Açık c.Reader.Read(p) zorunludur. Read içinde c.Read(p) yazmak kendini sonsuza kadar çağırırdı.
Bir test sahtesine interface gömmek yaygın bir kısayoldur: büyük interface'i gömün, testin ihtiyaç duyduğu tek metodu gerçekleştirin ve gerisini bırakın. Gerçekleştirilmemiş herhangi bir metot çağrılırsa nil pointer dereference ile panic olur; bir testte çoğu zaman istediğiniz de budur.
Gerçek bir yaygın kullanım: sync.Mutex gömmek
type Stats struct {
sync.Mutex
hits map[string]int
}
func (s *Stats) Hit(page string) {
s.Lock()
defer s.Unlock()
s.hits[page]++
}
Bu iyi okunur, ama Lock ve Unlock'u da Stats'ın API'sinin bir parçası olarak dışa açar, yani herhangi bir çağıran struct'ınızı kilitleyebilir. Paket dışında kullanılan tipler için adlandırılmış bir alan (mu sync.Mutex) kilidi özel tutar. Bu ödünleşim tüm gömmeler için geçerlidir: gömülü tipin dışa açtığı her şey tipinizin genel yüzeyinin bir parçası olur.
Sık yapılan hatalar
- Sanal dağıtım beklemek. Yükseltilen metotlar dış tipin geçersiz kılmalarını asla çağırmaz.
- Yükseltilen alanları bir literal'de ayarlamak. Gömülü tipin adını kullanın:
Admin{User: User{Name: "Ana"}}. - Nil gömülü pointer'lar. Gömülü bir
*Tya da interface, metotları çağrılmadan önce ayarlanmalıdır. - İstemeden oluşan API yüzeyi. Gömme, iç tipin dışa açık tüm metotlarını tipiniz üzerinde dışa açar.
Sıkça Sorulan Sorular
Go'da struct gömme nedir?
Bir alanı adı olmadan, yalnızca bir tiple tanımlamaktır: type Admin struct { User; Level int }. Gömülü User'ın alanları ve metotları yükseltilir, bu yüzden a.Name ve a.Greet() doğrudan bir Admin üzerinde çalışır. Gömülü değer yine normal bir alandır ve a.User olarak erişilebilir.
Go'da kalıtım var mı?
Hayır. Go'da sınıflar ve alt tip kalıtımı yoktur. Gömme, kompozisyon yoluyla kod yeniden kullanımı sağlar: dış tip iç tipin metotlarını alır, ama bir Admin bir User değildir. User beklenen yere bir Admin aktaramazsınız ve yükseltilen metotlar dış tipin metotlarını geri çağıramaz. Go'da çok biçimlilik interface'lerden gelir.
Go'da gömülü bir struct nasıl başlatılır?
Bir bileşik literal'de gömülü alanı tip adıyla belirtin: Admin{User: User{Name: "Ana"}, Level: 2}. Yükseltilen alanları literal'de doğrudan ayarlayamazsınız: Admin{Name: "Ana"}, unknown field Name in struct literal of type Admin hatasıyla başarısız olur.
Go'da bir struct'a interface gömülebilir mi?
Evet. Struct bu durumda interface'i gömülü değer üzerinden karşılar ve tek tek metotları geçersiz kılabilirsiniz. Decorator'larda ve test sahtelerinde yaygındır. Gömülü interface alanı nil ise geçersiz kılmadığınız bir metodu çağırmak nil pointer dereference ile panic'e yol açar.