13-bo‘lim

Interfeyslar

Yashirin qanoatlantirish, kichik interfeyslar, tur tasdiqlash va tur bo'yicha switch, any turi hamda nil interfeys tuzog'i.

🕑 14 daqiqa o‘qish 📄 750 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Birinchi interfeys
  2. Standart kutubxonadagi interfeyslar
  3. Bo'sh interfeys va any
  4. Tur tasdiqlash
  5. Tur bo'yicha switch
  6. Ko'rsatkich resiveri va interfeys
  7. nil interfeys tuzog'i
  8. Interfeysni qabul qiling, strukturani qaytaring
  9. Xulosa

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 #

Go
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)
}
Natija
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.

Yashirin qanoatlantirish nima beradi

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 #

Go
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())
}
Natija
Malika(88)
Natija: Malika(88)

fmt.Stringer - Go dagi eng mashhur interfeys, va u atigi bitta metoddan iborat:

Go
type Stringer interface {
    String() string
}

fmt.Fprintf esa io.Writer ni oladi - u ham bitta metodli. strings.Builder o'sha metodni yozgani uchun ishladi.

InterfeysMetodiQayerda
fmt.StringerString() stringChop etish
io.WriterWrite([]byte) (int, error)Yozish
io.ReaderRead([]byte) (int, error)O'qish
errorError() stringXatolar
sort.InterfaceLen, Less, SwapSaralash

Bo'sh interfeys va any #

Go
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)
}
Natija
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 #

Go
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)
}
Natija
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.

Comma-oksiz tasdiqlash vahima keltiradi
Go
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:

Go
if son, mos := q.(int); mos {
    fmt.Println(son * 2)
}

Tur bo'yicha switch #

Go
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))
}
Natija
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 #

Go
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())
}
Natija
2
Bu yerda & shart

var o Oshiruvchi = h deb yozsangiz, kompilyator xato beradi:

KOD
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 #

Go
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)
}
Natija
false
true
Go dagi eng mashhur tuzoq

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:

Go
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 #

Go
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"))
}
Natija
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.

Go ning asosiy maslahati

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.

Amaliy topshiriq
  1. Ikki metodli interfeys va uni qanoatlantiruvchi ikki tur yozing.
  2. Interfeys slicesi yasab, ustidan yuring.
  3. String() metodi bilan fmt.Stringer ni qanoatlantiring.
  4. any qabul qiluvchi funksiya yozing va %T bilan turni ko'ring.
  5. Comma-oksiz tur tasdiqlab, vahimani ko'ring.
  6. Comma-ok bilan xavfsiz tasdiqlang.
  7. Tur bo'yicha switch da interfeys shoxini ishlating.
  8. Ko'rsatkich resiverli turni interfeysga & siz berib ko'ring.
  9. nil ko'rsatkichni error sifatida qaytarib, tuzoqni ko'ring.
  10. Uni to'g'ri yozib, nil qaytishiga 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 switch da case sifatida interfeys ham yozilishi mumkin.
  • Ko'rsatkich resiverli metodlar bo'lsa, interfeysga &qiymat bering.
  • Interfeys ichida tur va qiymat saqlanadi; nil ko'rsatkich saqlangan interfeys nil emas.
  • Maslahat: interfeysni qabul qiling, strukturani qaytaring.

Keyingi bo'limda xatolar bilan ishlashni o'rganamiz.

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.