# 2.6 Interface ## Interface (Arabirim) Go' nun göze çarpan özelliklerinden biri interfacelerdir. Türkçeye arabirim diyerek de çevrilebilir. Bu bölümü okuduktan sonra, interfacelerin kullanımı muhtemelen anlamış olacaksınız. ### Interface nedir? Interface bir grup methodun özelliklerini, yapması gerekenleri tanımlamak için kullandığımız bir sooyutlama yöntemidir. Nesnelere uygulanacak kontrat ya da şartad olarak düşünebiliriz. Önceki bölümlerdeki gibi, Ogrenci hem de Calisan `Merhaba()` yapabilir, ancak aynı şeyi yapmış olmazlar. Ogrenciye `BorcPara()` , Calisana ise `MaasHarca () ` ve `SarkiSoyle ()` yöntemi ekleyeceğiz. Şimdi, Ogrenci'nin `Merhaba ()`,`SarkiSoyle () `ve` BorcPara () `olarak üç methodu, Calisan'ın ise `Merhaba () `,`SarkiSoyle () `ve` MaasHarca () ` methodları vardır. Bu yöntem topluluğuna interface(arairim) denir ve hem Ogrenci hem de Calisan tarafından uygulanır. Böylece, Ogrenci ve Calisan `Merhaba ()` ve `SarkiSoyle ()` interfacelerini uygulamış olur. Bununla birlikte, Calisan `BorcPara ()`, Ogrenci ise `MaasHarca ()` arayüzünü uygulamıyor. Bunun nedeni Calisan'ın `BorcPara ()` , Ogrenci'nin ise `MaasHarca ()` yöntemine sahip olmamasıdır. ### Interface Type'ları (türleri) Interface'ler aslında bir dizi methodtur yani eğer type, tüm interface methodlarını uygularsa biz ona interface'i implemente ediyor(uyguluyor) deriz. ```Go type Insan struct { ad string yas int telefon string } type Ogrenci struct { Insan okul string kredi float32 } type Calisan struct { Insan sirket string para float32 } // define interfaces type Adamlar interface { Merhaba() SarkiSoyle(sarkiSozu string) HunharcaYemek(beerStein string) } type Delikanli interface { Merhaba() SarkiSoyle(song string) BorcPara(amount float32) } type IhtiyarDelikanli interface { Merhaba() SarkiSoyle(song string) MaasHarca(amount float32) } func (i *Insan) Merhaba() { fmt.Printf("Selam, Ben %s beni şöyle çağırabilirsin %s\n", h.ad, h.telefon) } func (i *Insan) SarkiSoyle(sarkiSozu string) { fmt.Println("La la, la la la, la la la la la...", sarkiSozu) } func (i *Insan) HunharcaYemek(beerStein string) { fmt.Println("Yumm yumm yummm hunharca tüketiyorum...", beerStein) } // Calisan overloads Merhaba func (e *Calisan) Merhaba() { fmt.Printf("Selam, ben %s, %s 'de calısıyorum. Beni söyle cağırabilirsin %s\n", e.ad, e.sirket, e.telefon) } func (s *Ogrenci) BorcPara(amount float32) { s.kredi += amount } func (e *Calisan) MaasHarca(amount float32) { e.para -= amount } ``` Artık interface'in herhangi bir type(tür) tarafından uygulanabileceğini ve bir type'ın aynı anda birçok interface'i uygulayabileceğini biliyoruz. Herhangi bir type'ın boş `interface {}` uyguladığını unutmayın, çünkü herhangi bir methodu yoktur ve tüm typeların varsayılan olarak sıfır methodu vardır. ### Interface degeri Peki interface' e ne tür değerler koyulabilir? Bir değişkeni, type interface olarak tanımlarsak, interface'i uygulayan herhangi bir type bu değişkene atanabilir. Aşağıdaki örnekte olduğu gibi, Adamlar' a interface olarak bir "m" değişkeni tanımlarsak, Ogrenci, Insan veya Calisan'dan herhangi biri "m" değişkenine atanabilir. Ve elimizde Adamlar' dan bir parça(slice) olabilir. Ve Adamlar' ı implement eden(uygulayan) herhangi bir type'dan bir parça' da bu değişkene atanabilir. ```Go package main import "fmt" type Insan struct { ad string yas int telefon string } type Ogrenci struct { Insan okul string kredi float32 } type Calisan struct { Insan sirket string para float32 } // Interface Adamlar, Insan'dan implemente edildi. type Adamlar interface { Merhaba() SarkiSoyle(sarkiSozu string) } // method func (h Insan) Merhaba() { fmt.Printf("Selam, Ben %s, Beni şu numaradan arayabilrsin: %s\n", h.ad, h.telefon) } // method func (h Insan) SarkiSoyle(sarkiSozu string) { fmt.Println("La la la la...", sarkiSozu) } // method func (e Calisan) Merhaba() { fmt.Printf("Selam, ben %s, %s ' da çalışıyorum. Beni şu numaradan arayabilrsin: %s\n", e.ad, e.sirket, e.telefon) //Evet, iki satıra ayırabiliriz. } func main() { ali := Ogrenci{Insan{"Ali", 20, "222-222-XXX"}, "ODTU", 0.00} ahmet := Ogrenci{Insan{"Ahmet", 21, "111-222-XXX"}, "ITU", 100} mehmet := Calisan{Insan{"Mehmet", 36, "444-222-XXX"}, "Golang Inc.", 1000} veli := Calisan{Insan{"Veli", 36, "444-222-XXX"}, "Things Ltd.", 5000} // i interface'ini tanımlayalım. var i Adamlar //Ogrenci' yi tutabilirm i = ali fmt.Println("Öğrenci the Ali basliyor :D :") i.Merhaba() i.SarkiSoyle("November rain") //Calisan ' tutabilirim. i = veli fmt.Println("Veli bir Calisan:") i.Merhaba() i.SarkiSoyle("Uykusuz her gece, yorgun ölesiye!") // Adamlar 'dan parçalar/dilimler(slice) fmt.Println("Adamlar' ı dilimlere ayıralım ve n'olacağını görelim.") x := make([]Adamlar, 3) // Bu üç element farklı türdeler fakat hepsi Adamlar interface'ini uyguluyor/implement ediyor. x[0], x[1], x[2] = ali, ahmet, mehmet for _, value := range x { value.Merhaba() } } ``` Interface kendini implement edemez. ### Boş interface Boş interface, herhangi bir method içermeyen bir interface'dir. Bu nedenle tüm type'lar boş bir interface uygular. Bu durum, tüm türleri tek noktada saklamak istediğimizde çok kullanışlıdır ve C'deki void * 'e benzer. ```Go // boş interface uygulaması var bos interface{} // degiskenler i := 5 s := "Selam dünya" // herhangi bir türden veri tutabilir. bos = i bos = s ``` Eğer bir fonskiyon değişken türü olarak boş interface kullanıyorsa, herhangi bir türü kabul edebilir; bir fonksiyon dönüş değeri türü olarak boş interface kullanıyorsa, herhangi bir türü döndürebilir. ### Interface'in method argümanları Herhangi bir değişken bir arayüzde kullanılabilir. Peki bu özelliği herhangi bir değişkeni bir fonksiyona geçirmek için nasıl kullanabiliriz? Örneğin, fmt.Println'i çok kullanıyoruz, ancak herhangi bir argümanı kabul edebileceğini hiç fark ettiniz mi? "Fmt" nin açık kaynak koduna baktığımızda, aşağıdaki tanımı görüyoruz. ```Go type Stringer interface { String() string } ``` Bu, Stringer interface'ini uygulayan herhangi bir türün bağımsız değişken olarak fmt.Println'ye geçirilebileceği anlamına gelir. Kanıtlayalım. ```Go package main import ( "fmt" "strconv" ) type Insan struct { ad string yas int telefon string } // Insan implements fmt.Stringer func (h Insan) String() string { return "ad:" + h.ad + ", yas:" + strconv.Itoa(h.yas) + ", Contact:" + h.telefon } func main() { isik := Insan{"Isik", 25, "000-7777-XXX"} fmt.Println("Bu kişi: ", isik) } ``` Asagıdaki box örneğine baktığımızda, Color'ın Stringer interface'ini de uyguladığını göreceksiniz, böylece yazdırma biçimini özelleştirebiliyoruz. Eğer ki bu interface'i uygulamazsak, fmt.Println türü varsayılan biçimiyle yazdırır. ```Go fmt.Println("En büyük", boxes.BiggestsColor().String()) fmt.Println("En büyük", boxes.BiggestsColor()) ``` Uyarı: Eğer type ` error interface'ini uyguladıysa, fmt `Error () ' olarak adlandırır, bu nedenle bu noktada Stringer uygulamasına gerek kalmaz. ### Interface' in değişken türü Eğer değişken, interface'i uygulayan type'sa aynı interface'i uygulayan başka herhangi bir type'ın bu değişkene atanabileceğini biliyoruz. Soru, interface'de saklanan belirli türü nasıl bilebiliriz? Size göstereceğim iki yol var. - Comma-ok pattern Go syntax'ına uygun olarak şöyle bir şey yapılabilir `deger, ok := element.(T)`. Değişkenin beklediğimiz tür olup olmadığını kontrol eder; burada "değer", değişkenin değeri, "ok", boolean türünden bir değişkeni, "element" interface değişkeni ve T, yerleştirmenin türüdür . Element beklediğimiz türse, "ok" true, değilse false olur. Daha iyi anlaşılması için biraz daha örnek verelim. ```Go package main import ( "fmt" "strconv" ) type Element interface{} type List []Element type Person struct { ad string yas int } func (p Person) String() string { return "(ad: " + p.ad + " - yas: " + strconv.Itoa(p.yas) + " years)" } func main() { list := make(List, 3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis", 70} for index, element := range list { if value, ok := element.(int); ok { fmt.Printf("list[%d] 'in türü int and degeri: %d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] 'in string int and degeri: %s\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] 'in türü Person and degeri: %s\n", index, value) } else { fmt.Printf("list[%d] 'in bambaşka bir türü var... \n", index) } } } ``` Bu kalıbı/pattern'i kullanmak oldukça kolaydır, ancak test etmek için birçok türümüz varsa, `switch` i kullansak iyi olur. - switch test Hadi `switch` kullanarak üstteki örneği yeniden yazalım. ```Go package main import ( "fmt" "strconv" ) type Element interface{} type List []Element type Person struct { ad string yas int } func (p Person) String() string { return "(ad: " + p.ad + " - yas: " + strconv.Itoa(p.yas) + " years)" } func main() { list := make(List, 3) list[0] = 1 //an int list[1] = "Selam" //a string list[2] = Person{"isik", 25} for index, element := range list { switch value := element.(type) { case int: fmt.Printf("list[%d] bir int and ve değeri %d\n", index, value) case string: fmt.Printf("list[%d] bir string and ve değeri %s\n", index, value) case Person: fmt.Printf("list[%d] bir Person and ve değeri %s\n", index, value) default: fmt.Println("list[%d] bambaşka bir türe(tyoe) sahip", index) } } } ``` Unutmamalıyız ki `element. (Type)`, `switch` gövdesinin dışında kullanılamaz, bu durumda`virgül-ok` desenini kullanmanız gerektiği anlamına gelir. ### Embedded interfaces Go' daki güzel şeylerden biri de structlardaki anonim alanlar gibi birçok yerleşik mantık sözdizimine sahip olmasıdır. Şaşırtıcı olmayan bir şekilde, interfaceleri anonim alanlar olarak da kullanabiliriz, ancak onlara `Gömülü(embedded) interface` diyoruz. Burada anonim alanlarla aynı kuralları uygularız. Daha spesifik olarak, bir interface'in içinde gömülü başka bir interface varsa, gömülü interface'in sahip olduğu tüm yöntemlere sahipmiş gibi davranacaktır. `Container / heap` içindeki kaynak kodların aşağıdaki gibi olduğunu görebiliriz: ```Go type Interface interface { sort.Interface // embedded sort.Interface Push(x interface{}) //a Push method to push elements into the heap Pop() interface{} //a Pop method that pops elements from the heap } ``` `Sort.Interface` öğesinin gömülü bir interface olduğunu görüyoruz, bu nedenle yukarıdaki Interface'de kapalı olarak "sort.Interface" içinde bulunan üç yöntem vardır. ```Go type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) } ``` Başka bir örneği de `io` paketi içinde bulunan`io.ReadWriter` 'de görebiliriz. ```Go // io.ReadWriter type ReadWriter interface { Reader Writer } ``` ### Reflection(Yansıma) Go'daki yansıma, çalışma zamanında bilgileri belirlemek için kullanılır. `reflect` paketini, [Yansıma kuralları](http://golang.org/doc/articles/laws_of_reflection.html) yansımanın nasıl calıstıgına baktıktan sonra göreceğiz. Yansıtmayı kullanırken üç adım söz konusudur. İlk olarak, bir interface'i türleri yansıtacak şekilde dönüştürmeliyiz (reflect.Type veya reflect.Value bu duruma bağlıdır). ```Go t := reflect.TypeOf(i) // i'nin içindeki meta veriyi al ve tüm öğeleri almak için t'yi kullan v := reflect.ValueOf(i) // type i 'nin içindeki gerçek değeri al,ve v' yi değiştirmek için değerini kullan. ``` Bundan sonra, ihtiyaç duyduğumuz değerleri elde etmek için yansıyan türleri dönüştürebiliriz. ```Go var x float64 = 3.4 t := reflect.TypeOf(x) v := reflect.ValueOf(x) fmt.Println("type:", t) fmt.Println("value:", v) fmt.Println("kind float64 mu?:", v.Kind() == reflect.Float64) ``` Son olarak, yansıyan türlerin değerlerini değiştirmek istiyorsak, onu değiştirilebilir yapmamız gerekir. Daha önce tartışıldığı gibi, değere göre geçiş ile referansa göre geçiş arasında bir fark vardır. Aşağıdaki kod derlenmeyecektir. ```Go var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) ``` Bunun yerine, yansıma türlerinden değerleri değiştirmek için aşağıdaki kodu kullanmalıyız. ```Go var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1) ``` Yansımanın temellerini az önce tartıştık, ancak daha fazlasını anlamak için daha fazla pratik yapmalısınız. ## Links - [Directory](preface.md) - Previous section: [Nesne-Yönelim](02.5.md) - Next section: [Eş-zamanlılık](02.7.md)