Generics'e Giriş
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 67 / 107.
Go 1.18'den önce, farklı türler için yeniden kullanılabilir kod yazmak, ya fonksiyonları çoğaltmak ya da tür onaylamaları (type assertions) ile boş arayüz any kullanmak anlamına geliyordu. Generic'ler, tam tür güvenliğini korurken birden fazla türle çalışan fonksiyonlar ve türler yazmanıza olanak tanıyarak bu sorunu çözer.
Bir slice içindeki minimum değeri bulmayı düşünün. Generic yapılar olmadan, her tür için ayrı fonksiyonlara ihtiyacınız olurdu:
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Generic'ler ile, mantığı köşeli parantezler içindeki bir tip parametresi kullanarak bir kez yazarsınız:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}[T int | float64], hem int hem de float64 olabilen bir T tip parametresi tanımlar. Artık aynı fonksiyonla Min([]int{3, 1, 2}) veya Min([]float64{3.5, 1.2}) çağrısı yapabilirsiniz. Derleyici, tipleri derleme zamanında kontrol eder, böylece çalışma zamanı yükü olmadan güvenlik elde edersiniz.
Gelecek derslerde tür parametrelerini, kısıtlamaları ve generic struct'ları ayrıntılı olarak inceleyeceğiz.
Görev
KolayGeneric yapıları kullanarak hem tam sayı hem de ondalıklı sayı puanlarıyla çalışan bir puan analizörü oluşturalım! Farklı sayısal türler için yinelenen kod ihtiyacını ortadan kaldırarak, bir dilimdeki (slice) maksimum değeri bulabilen tek bir generic fonksiyon oluşturacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
analyzer.go: Generic analiz fonksiyonunuzu tanımlayın.Bir dilimdeki maksimum değeri bulan ve döndüren generic bir
Max[T int | float64](values []T) Tfonksiyonu oluşturun. Fonksiyon,Ttür parametresi aracılığıyla heminthem defloat64türleriyle çalışmalıdır.Ayrıca bir dilimdeki tüm değerlerin toplamını hesaplayan ve döndüren generic bir
Sum[T int | float64](values []T) Tfonksiyonu oluşturun.main.go: Girdiyi okuyun ve generic fonksiyonlarınızı farklı türlerle test edin.Bir tür göstergesi (
intveyafloat) okuyun, ardından bir adet sayısı ve ardından o kadar değer okuyun. Değerleri türe göre ayrıştırın, uygun türle hemMaxhem deSumfonksiyonlarını çağırın ve sonuçları yazdırın.Tam sayı girdisi için şunu yazdırın:
Max (int): [value] Sum (int): [value]Ondalıklı sayı girdisi için bir ondalık basamakla yazdırın:
Max (float): [value] Sum (float): [value]
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Tür göstergesi (
intveyafloat) - Satır 2: Değerlerin sayısı (tam sayı)
- Sonraki satırlar: Her satırda bir değer
Örneğin, şu girdi verildiğinde:
int
4
15
8
23
11Çıktınız şöyle olmalıdır:
Max (int): 23
Sum (int): 57Ve şu girdi verildiğinde:
float
3
4.5
9.2
6.8Çıktınız şöyle olmalıdır:
Max (float): 9.2
Sum (float): 20.5Aynı generic fonksiyonların hem tam sayı hem de ondalıklı sayı verilerini nasıl işlediğine dikkat edin; [T int | float64] tür parametresi, siz mantığı yalnızca bir kez yazarken derleyicinin her kullanım için tür açısından güvenli kod oluşturmasına olanak tanır.
Kopya kağıdı
Generic yapılar, tip güvenliğini korurken birden fazla tiple çalışan yeniden kullanılabilir kodlar yazmanıza olanak tanır. Go 1.18'den önce, her tip için ayrı fonksiyonlara ihtiyacınız vardı veya tip onaylamaları (type assertions) ile boş arayüz (empty interface) kullanıyordunuz.
Tip parametreleri köşeli parantez içinde bildirilir ve bir generic fonksiyonun hangi tipleri kabul edebileceğini belirtir:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}[T int | float64] söz dizimi, int veya float64 olabilen bir T tip parametresi tanımlar. Boru | operatörü, izin verilen tipleri listeler.
Generic fonksiyonları farklı tiplerle çağırabilirsiniz:
Min([]int{3, 1, 2}) // int ile çalışır
Min([]float64{3.5, 1.2}) // float64 ile çalışırDerleyici, tip kontrolünü derleme zamanında gerçekleştirerek çalışma zamanı yükü (runtime overhead) olmadan güvenlik sağlar.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Tür göstergesini oku
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Sayısı oku
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Tam sayı değerlerini oku
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Max ve Sum fonksiyonlarını integer slice ile çağır
// TODO: Sonuçları "Max (int): [value]" ve "Sum (int): [value]" formatında yazdır
} else if typeStr == "float" {
// Ondalıklı sayı değerlerini oku
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Max ve Sum fonksiyonlarını float64 slice ile çağır
// TODO: Sonuçları virgülden sonra bir basamak olacak şekilde "Max (float): [value]" ve "Sum (float): [value]" formatında yazdır
}
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool