13-bo‘lim
Interfeyslar
Yashirin qanoatlantirish, kichik interfeyslar, tur tasdiqlash va tur bo'yicha switch, any turi hamda nil interfeys tuzog'i.
Ushbu bo‘lim mundarijasi
Interfeys - bu metodlar ro'yxati. Turi shu metodlarni yozgan bo'lsa, u interfeysni qanoatlantiradi.
Go dagi eng muhim farq: buni hech qayerda e'lon qilish kerak emas.
Birinchi interfeys #
package main
import "fmt"
type Shakl interface {
Yuzasi() float64
Nomi() string
}
type Kvadrat struct {
Tomon float64
}
func (k Kvadrat) Yuzasi() float64 { return k.Tomon * k.Tomon }
func (k Kvadrat) Nomi() string { return "kvadrat" }
type Doira struct {
Radius float64
}
func (d Doira) Yuzasi() float64 { return 3.14159 * d.Radius * d.Radius }
func (d Doira) Nomi() string { return "doira" }
func chiqar(s Shakl) {
fmt.Printf("%s: %.2f\n", s.Nomi(), s.Yuzasi())
}
func main() {
chiqar(Kvadrat{Tomon: 4})
chiqar(Doira{Radius: 2})
shakllar := []Shakl{Kvadrat{Tomon: 1}, Doira{Radius: 1}}
jami := 0.0
for _, s := range shakllar {
jami += s.Yuzasi()
}
fmt.Printf("Jami: %.2f\n", jami)
}
kvadrat: 16.00
doira: 12.57
Jami: 4.14
Diqqat qiling: Kvadrat hech qayerda "men Shakl man" demaydi.
U shunchaki kerakli metodlarni yozgan - shuning uchun mos keladi.
Java da class Kvadrat implements Shakl deb yozish kerak.
Bu bog'liqlikni teskari qiladi: Kvadrat Shakl ni bilishi shart.
Go da esa interfeys ishlatuvchi tomonda e'lon qilinadi. Ya'ni siz begona kutubxonadagi tur uchun ham interfeys yozib, uni darhol ishlatishingiz mumkin - o'sha kutubxonaga tegmasdan.
Aynan shuning uchun Go da interfeyslar odatda kichik bo'ladi: bitta yoki ikkita metod. Katta interfeys ko'p narsani talab qiladi va uni qanoatlantirish qiyinlashadi.
Standart kutubxonadagi interfeyslar #
package main
import (
"fmt"
"strings"
)
type Talaba struct {
Ism string
Ball int
}
func (t Talaba) String() string {
return fmt.Sprintf("%s(%d)", t.Ism, t.Ball)
}
func main() {
t := Talaba{Ism: "Malika", Ball: 88}
fmt.Println(t)
var quruvchi strings.Builder
fmt.Fprintf(&quruvchi, "Natija: %v", t)
fmt.Println(quruvchi.String())
}
Malika(88)
Natija: Malika(88)
fmt.Stringer - Go dagi eng mashhur interfeys, va u atigi bitta
metoddan iborat:
type Stringer interface {
String() string
}
fmt.Fprintf esa io.Writer ni oladi - u ham bitta metodli.
strings.Builder o'sha metodni yozgani uchun ishladi.
| Interfeys | Metodi | Qayerda |
|---|---|---|
fmt.Stringer | String() string | Chop etish |
io.Writer | Write([]byte) (int, error) | Yozish |
io.Reader | Read([]byte) (int, error) | O'qish |
error | Error() string | Xatolar |
sort.Interface | Len, Less, Swap | Saralash |
Bo'sh interfeys va any #
package main
import "fmt"
func tavsifla(q any) {
fmt.Printf("%v (%T)\n", q, q)
}
func main() {
tavsifla(42)
tavsifla("Namangan")
tavsifla([]int{1, 2})
tavsifla(nil)
}
42 (int)
Namangan (string)
[1 2] ([]int)
<nil> (<nil>)
interface{} - metodsiz interfeys. Har qanday tur nol ta
metodga ega, shuning uchun hamma narsa unga mos keladi.
any - uning boshqa nomi, Go 1.18 da qo'shilgan. Yangi kodda
any yoziladi.
Tur tasdiqlash #
package main
import "fmt"
func main() {
var q any = "Namangan"
matn := q.(string)
fmt.Println(matn, len(matn))
son, mos := q.(int)
fmt.Println(son, mos)
matn2, mos2 := q.(string)
fmt.Println(matn2, mos2)
}
Namangan 8
0 false
Namangan true
q.(string) - "bu aslida string deb o'yla" degani.
10-bo'limdagi comma-ok shakli bu yerda ham ishlaydi va u xavfsizroq.
var q any = "Namangan"
son := q.(int) // panic: interface conversion
Ikkinchi qiymatni olmasangiz va tur mos kelmasa, dastur darhol to'xtaydi.
Shuning uchun turdan ishonchingiz komil bo'lmasa, har doim comma-ok shaklini ishlating:
if son, mos := q.(int); mos {
fmt.Println(son * 2)
}
Tur bo'yicha switch #
package main
import "fmt"
func olcham(q any) string {
switch v := q.(type) {
case string:
return fmt.Sprintf("satr, %d bayt", len(v))
case int:
return fmt.Sprintf("son, kvadrati %d", v*v)
case []int:
return fmt.Sprintf("ro'yxat, %d element", len(v))
case fmt.Stringer:
return "String metodi bor: " + v.String()
default:
return fmt.Sprintf("boshqa tur: %T", v)
}
}
type Pul struct{ Som int }
func (p Pul) String() string { return fmt.Sprintf("%d so'm", p.Som) }
func main() {
fmt.Println(olcham("salom"))
fmt.Println(olcham(7))
fmt.Println(olcham([]int{1, 2, 3}))
fmt.Println(olcham(Pul{Som: 5000}))
fmt.Println(olcham(3.14))
}
satr, 5 bayt
son, kvadrati 49
ro'yxat, 3 element
String metodi bor: 5000 so'm
boshqa tur: float64
case da interfeys ham yozilishi mumkin: fmt.Stringer shoxi
String() metodi bor har qanday turni ushlaydi.
Shoxlar yuqoridan pastga tekshiriladi, shuning uchun aniqroq turlarni yuqoriga qo'ying.
Ko'rsatkich resiveri va interfeys #
package main
import "fmt"
type Hisoblagich struct {
Soni int
}
func (h *Hisoblagich) Oshir() { h.Soni++ }
func (h Hisoblagich) Qiymat() int {
return h.Soni
}
type Oshiruvchi interface {
Oshir()
}
func main() {
h := Hisoblagich{}
var o Oshiruvchi = &h
o.Oshir()
o.Oshir()
fmt.Println(h.Qiymat())
}
2
var o Oshiruvchi = h deb yozsangiz, kompilyator xato beradi:
Hisoblagich does not implement Oshiruvchi
(method Oshir has pointer receiver)
12-bo'limda Go & ni o'zi qo'yishini aytgan edik. Lekin bu faqat
to'g'ridan-to'g'ri chaqiruvda ishlaydi.
Interfeysga saqlashda esa Go yordam bera olmaydi: interfeys ichida
nusxa saqlansa, Oshir() o'sha nusxani o'zgartirardi va o'zgarish
yo'qolardi.
Qoida: ko'rsatkich resiverli metodlar bo'lsa, interfeysga
ko'rsatkich bering - &h.
nil interfeys tuzog'i #
package main
import "fmt"
type OzXato struct {
Sabab string
}
func (x *OzXato) Error() string {
return x.Sabab
}
func buzuq() error {
var x *OzXato
return x
}
func toliq() error {
return nil
}
func main() {
fmt.Println(buzuq() == nil)
fmt.Println(toliq() == nil)
}
false
true
buzuq() funksiyasi nil ko'rsatkich qaytardi, lekin natija
nil emas.
Sababi: interfeys qiymati ikki qismdan iborat - tur va
qiymat. buzuq() da qiymat nil, lekin tur *OzXato.
Interfeys faqat ikkalasi ham bo'sh bo'lganda nil bo'ladi.
Bu real dasturlarda jiddiy xatoga olib keladi: funksiya "xato
yo'q" deb o'ylaydi, chaqiruvchi esa xato != nil ni rost deb
topadi va bo'lmagan xatoni qayta ishlaydi.
Yechim oddiy - error turidagi o'zgaruvchi qaytarmang:
func togri() error {
var x *OzXato
if x == nil {
return nil // ochiq nil
}
return x
}
Yoki umuman *OzXato turidagi o'zgaruvchi saqlamang: xato bo'lsa
darhol return &OzXato{...}, bo'lmasa return nil.
Interfeysni qabul qiling, strukturani qaytaring #
package main
import (
"fmt"
"strings"
)
func yoz(w *strings.Builder, qatorlar []string) {
for _, q := range qatorlar {
fmt.Fprintln(w, q)
}
}
func main() {
var b strings.Builder
yoz(&b, []string{"birinchi", "ikkinchi"})
fmt.Print(b.String())
fmt.Println(strings.Count(b.String(), "\n"))
}
birinchi
ikkinchi
2
Yuqoridagi yoz funksiyasi aniq turni - *strings.Builder ni
oladi. Uni io.Writer qilsak, u faylga ham, tarmoqqa ham, ekranga
ham yoza olardi.
Rasmiy tavsiya shunday yangraydi: "interfeyslarni qabul qiling, strukturalarni qaytaring".
Sabab: funksiya kirishida interfeys bo'lsa, uni ko'proq joyda ishlatish mumkin. Chiqishida aniq tur bo'lsa, chaqiruvchi uning hamma imkoniyatidan foydalana oladi.
Yana bir muhim maslahat: interfeysni oldindan yozmang. Avval aniq turlar bilan ishlang, keyin ikkinchi amalga oshirish paydo bo'lganda interfeysni ajratib oling.
Go jamoasida mashhur ibora bor: interfeysni kerak bo'lganda kashf etasiz, rejalashtirganda emas.
- Ikki metodli interfeys va uni qanoatlantiruvchi ikki tur yozing.
- Interfeys slicesi yasab, ustidan yuring.
String()metodi bilanfmt.Stringerni qanoatlantiring.anyqabul qiluvchi funksiya yozing va%Tbilan turni ko'ring.- Comma-oksiz tur tasdiqlab, vahimani ko'ring.
- Comma-ok bilan xavfsiz tasdiqlang.
- Tur bo'yicha
switchda interfeys shoxini ishlating. - Ko'rsatkich resiverli turni interfeysga
&siz berib ko'ring. nilko'rsatkichnierrorsifatida qaytarib, tuzoqni ko'ring.- Uni to'g'ri yozib,
nilqaytishiga erishing.
Xulosa #
- Interfeys - metodlar ro'yxati; tur uni yashirin ravishda qanoatlantiradi.
- Interfeys ishlatuvchi tomonda e'lon qilinadi - shuning uchun begona turlar uchun ham yozish mumkin.
- Go da interfeyslar kichik: eng mashhurlari bitta metoddan iborat.
any- metodsiz interfeys; hamma narsa unga mos keladi.- Tur tasdiqlashda comma-ok shaklini ishlating - aks holda vahima.
- Tur bo'yicha
switchdacasesifatida interfeys ham yozilishi mumkin. - Ko'rsatkich resiverli metodlar bo'lsa, interfeysga
&qiymatbering. - Interfeys ichida tur va qiymat saqlanadi;
nilko'rsatkich saqlangan interfeysnilemas. - Maslahat: interfeysni qabul qiling, strukturani qaytaring.
Keyingi bo'limda xatolar bilan ishlashni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.