Menu

Golang Struct Gömme: Embedding ve Yükseltilen Metotlar

Gömme (embedding), bir tipi alan adı olmadan başka bir tipin içine koyar, böylece alanları ve metotları dıştaki tipe yükseltilir. Yükseltmenin nasıl çalıştığını, interface gömmeyi, ad çakışmalarını ve gömmenin neden kalıtım olmadığını öğrenin.

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

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 *T ya 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.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA