Türler
Önceki veri türleri bölümünde Go'daki tüm yerleşik veri türleri kısaca tanıtılmıştı. Bu yerleşik temel türler, sonraki özel türlerin temelini oluşturur. Go tipik bir statik tür dilidir. Tüm değişkenlerin türleri derleme zamanında belirlenir ve programın yaşam döngüsü boyunca değişmez. Bu bölümde Go'nun tür sistemi ve temel kullanımı kısaca tanıtılacaktır.
Statik Güçlü Tip
Go statik güçlü tip bir dildir. Statik, Go'nun tüm değişken türlerinin derleme sırasında zaten belirlendiği ve programın yaşam döngüsü boyunca bir daha değişmeyeceği anlamına gelir. Go'daki kısa değişken bildirimi dinamik dillerin yazımına benzese de, değişken türü derleyici tarafından otomatik olarak çıkarılır. En temel fark, türü bir kez çıkarıldıktan sonra bir daha değişmemesidir. Dinamik diller ise tam tersidir. Bu nedenle aşağıdaki kod derlenemez. Çünkü a int türünde bir değişkendir, string atanamaz.
func main() {
var a int = 64
a = "64"
fmt.Println(a) // cannot use "64" (untyped string constant) as int value in assignment
}Güçlü tip, programda sıkı tür denetimi yapıldığı anlamına gelir. Tür eşleşmezse, programcıya bunu yapmaması gerektiği hemen söylenir. Dinamik diller gibi olası sonuçları çıkarmaya çalışmaz. Bu nedenle aşağıdaki kod derlenemez. Çünkü her iki tür farklıdır, işlem yapılamaz.
func main() {
fmt.Println(1 + "1") // invalid operation: 1 + "1" (mismatched types untyped int and untyped string)
}Tür Son Ekleme
Go neden tür bildirimini öne değil de sona koyar? Büyük ölçüde C dilinden ders aldığı içindir. Resmi bir örnekle etkiyi gösterelim. Bu bir fonksiyon işaretçisidir
int (*(*fp)(int (*)(int, int), int))(int, int)Dürüst olmak gerekirse, dikkatli bakmazsanız bunun ne tür olduğunu anlamak zordur. Go'da benzer yazım şu şekildedir
f func(func(int,int) int, int) func(int, int) intGo'nun bildirim yöntemi her zaman isim önde, tür sonda ilkesini izler. Soldan sağa okuyarak, ilk bakışta bunun bir fonksiyon olduğunu ve dönüş değerinin func(int,int) int olduğunu anlayabilirsiniz. Tür daha karmaşıklaştığında, tür son ekleme okunabilirlik açısından çok daha iyidir. Go birçok düzeyde tasarımı okunabilirlik için hizmet eder.
Tür Bildirimi
Go'da tür bildirimi kullanarak özel adlı yeni bir tür bildirebilirsiniz. Yeni bir tür bildirmek genellikle bir tür adı ve bir temel tür gerektirir. Basit örnek aşağıdaki gibidir:
type MyInt int64Yukarıdaki tür bildiriminde, type anahtar kelimesi ile temel türü int64 olan MyInt adında bir tür bildirilmiştir. Go'da, her yeni bildirilen türün bir temel türe sahip olması gerekir. Ve tür adının mevcut yerleşik tanımlayıcılarla tekrarlanması önerilmez.
type MyInt int64
type MyFloat64 float64
type MyMap map[string]int
// Derlenebilir, ancak kullanılması önerilmez, bu mevcut türü geçersiz kılar
type int int64Tür bildirimi ile bildirilen türler yeni türlerdir. Farklı türler işlem yapamaz. Temel tür aynı olsa bile.
type MyFloat64 float64
var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(f1 + f)invalid operation: f1 + f (mismatched types MyFloat64 and float64)Tür Takma Adı
Tür takma adı tür bildiriminden farklıdır. Tür takma adı sadece bir takma addır, yeni bir tür oluşturmaz. Basit örnek aşağıdaki gibidir:
type Int = intHer ikisi de aynı türdür, sadece adları farklıdır. Bu nedenle işlem yapabilirler. Aşağıdaki örnek doğal olarak derlenebilir.
type Int = int
var a Int = 1
var b int = 2
fmt.Println(a + b)3Tür takma adı özellikle karmaşık türler için çok kullanışlıdır. Örneğin şimdi bir tür map[string]map[string]int var. Bu iki boyutlu bir map'tir. Şimdi bir fonksiyon parametresi map[string]map[string]int türündedir. Aşağıdaki gibi
func PrintMyMap(mymap map[string]map[string]int) {
fmt.Println(mymap)
}Bu durumda, tür bildirimi kullanmaya gerek yoktur. Çünkü ilki yeni bir tür bildirir, bu fonksiyonun parametresi olarak kullanılamaz. Tür takma adı kullanıldıktan sonra örnek şu şekildedir
type TwoDMap = map[string]map[string]int
func PrintMyMap(mymap TwoDMap) {
fmt.Println(mymap)
}Tür takma adı kullandıktan sonra daha basit görünür.
::: ipucu
Yerleşik tür any, interface{}'nin tür takma adıdır. Her ikisi tamamen eşdeğerdir, sadece adları farklıdır.
:::
Tür Dönüşümü
Go'da sadece açık tür dönüşümü vardır, örtük tür dönüşümü yoktur. Bu nedenle farklı türdeki değişkenler işlem yapamaz, parametre olarak aktarılamaz. Tür dönüşümü için ön koşul, dönüştürülen değişkenin türünün ve dönüştürülecek hedef türün bilinmesidir. Örnek aşağıdaki gibidir:
type MyFloat64 float64
var f1 MyFloat64
var f float64
f1 = 0.2
f = 0.1
fmt.Println(float64(f1) + f)0.30000000000000004MyFloat64'ı açıkça float64 türüne dönüştürerek toplama işlemi yapılabilir. Tür dönüşümünün bir diğer ön koşulu: dönüştürülen türün hedef tür tarafından temsil edilebilir (Representability) olmasıdır. Örneğin int, int64 türü tarafından temsil edilebilir, float64 türü tarafından da temsil edilebilir. Bu nedenle açık tür dönüşümü yapabilirler. Ancak int türü string ve bool türleri tarafından temsil edilemez. Bu nedenle tür dönüşümü yapamazlar.
::: ipucu
Temsil (Representability) tanımı için Referans El Kitabı - Representability adresine giderek daha fazla detay öğrenebilirsiniz.
:::
İki tür birbirini temsil edebilse bile, tür dönüşümünün sonucu her zaman doğru değildir. Aşağıdaki örneğe bakalım:
var num1 int8 = 1
var num2 int32 = 512
fmt.Println(int32(num1), int8(num2))1 0num1 doğru şekilde int32 türüne dönüştürüldü, ancak num2 dönüştürülemedi. Bu tipik bir taşma sorunudur. int32 31 bit tamsayıyı temsil edebilir, int8 sadece 7 bit tamsayıyı temsil edebilir. Yüksek hassasiyetli tamsayı düşük hassasiyetli tamsayıya dönüştürülürken yüksek basamaklar atılır ve düşük basamaklar saklanır. Bu nedenle num1'in dönüşüm sonucu 0'dır. Sayı tür dönüşümünde, genellikle küçüğün büyüğe dönüştürülmesi önerilir, büyüğün küçüğe dönüştürülmesi önerilmez.
Tür dönüşümü kullanırken, bazı türler için belirsizlikten kaçınılmalıdır. Örnek aşağıdaki gibidir:
*Point(p) // *(Point(p)) ile eşdeğerdir
(*Point)(p) // p'yi *Point türüne dönüştürür
<-chan int(c) // <-(chan int(c)) ile eşdeğerdir
(<-chan int)(c) // c'yi <-chan int türüne dönüştürür
(func())(x) // x'i func() türüne dönüştürür
(func() int)(x) // x'i func() int türüne dönüştürürTür İddiası
Tür iddiası genellikle bir interface türündeki değişkenin belirli bir türe ait olup olmadığını belirlemek için kullanılır. Örnek aşağıdaki gibidir
var b int = 1
var a interface{} = b
if intVal, ok := a.(int); ok {
fmt.Println(intVal)
} else {
fmt.Println("error type")
}1interface{} boş interface türü olduğundan, boş interface türü tüm türleri temsil edebilir. Ancak int türü interface{} türünü temsil edemez. Bu nedenle tür dönüşümü kullanılamaz. Tür iddiası alt türün istenen tür olup olmadığını belirleyebilir. Tür iddiası ifadesinin iki dönüş değeri vardır. Biri tür dönüştürülmüş değer, diğeri dönüşüm sonucunun boolean değeridir.
Tür Yargısı
Go'da, switch ifadesi özel bir yazımı da destekler. Bu yazım ile farklı case'lere göre farklı mantık işleme yapılabilir. Kullanım ön koşulu giriş parametresinin interface türü olmasıdır. Örnek aşağıdaki gibidir:
var a interface{} = 2
switch a.(type) {
case int: fmt.Println("int")
case float64: fmt.Println("float")
case string: fmt.Println("string")
}int::: ipucu
unsafe paketi tarafından sağlanan işlemlerle, Go'nun tür sistemini atlayabilirsiniz. Orijinal olarak derlenemeyen tür dönüşümü işlemlerini yapabilirsiniz.
:::
