Go’da metodlar
Metod — ma’lum turga bog‘langan funksiya. U ma’lumot va shu ma’lumot ustidagi amalni bir joyda ifodalashga yordam beradi. Oddiy funksiyadan farqi, metod nomidan oldin receiver yoziladi.
Value receiver
package main
import "fmt"
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
rectangle := Rectangle{Width: 5, Height: 3}
fmt.Println("Yuza:", rectangle.Area())
}
Natija:
(r Rectangle) receiver bo‘lib, metod qaysi turga tegishli ekanini bildiradi. r qiymatning nusxasi. Faqat o‘qiydigan va kichik turlarda value receiver tabiiy tanlov bo‘lishi mumkin.
Metod faqat shu paketda e’lon qilingan nomlangan turga biriktiriladi. Boshqa paketdagi time.Time kabi turga bevosita yangi metod qo‘shib bo‘lmaydi.
Oddiy turga metod qo‘shish
Metod faqat struct uchun emas, shu paketda e’lon qilingan har qanday nomlangan tur uchun yozilishi mumkin:
package main
import "fmt"
type Celsius float64
func (c Celsius) Fahrenheit() float64 {
return float64(c)*9/5 + 32
}
func main() {
temperature := Celsius(25)
fmt.Println(temperature.Fahrenheit())
}
Natija:
Celsiusning asosiy turi float64, lekin u alohida nomlangan tur. Shu sababli unga Fahrenheit() metodini biriktirish mumkin. Oddiy alias bunga yaramaydi:
Bu yozuv yangi tur yaratmaydi. U faqat float64 uchun boshqa nom beradi.
Pointer receiver
Asl qiymat o‘zgarishi kerak bo‘lsa, pointer receiver ishlatiladi:
package main
import "fmt"
type Account struct {
Balance int64
}
func (a *Account) Deposit(amount int64) error {
if a == nil {
return fmt.Errorf("account nil")
}
if amount <= 0 {
return fmt.Errorf("amount musbat bo‘lishi kerak")
}
a.Balance += amount
return nil
}
func main() {
account := Account{Balance: 100_000}
if err := account.Deposit(25_000); err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println(account.Balance)
}
Natija:
a.Balance yozuvida Go avtomatik dereference qiladi; (*a).Balance deb yozish shart emas. account manzili olinadigan qiymat bo‘lgani uchun account.Deposit(...) chaqiruvini kompilyator (&account).Deposit(...) sifatida qabul qiladi.
Pointer receiver nil bilan chaqirilishi mumkin. Metodning vazifasiga qarab nil tekshiriladi yoki bu holat taqiqlanadi. Yuqoridagi metod xato qaytarib xavfsiz yakunlanadi.
Metod orqali holatni boshqarish
Metodlar struct maydonlarini bevosita ochiq qoldirmasdan boshqarishga yordam beradi. Masalan, hisob balansiga noto‘g‘ri qiymat yozilishini metod ichida cheklash mumkin:
package main
import (
"errors"
"fmt"
)
type Account struct {
balance int64
}
func (a *Account) Deposit(amount int64) error {
if amount <= 0 {
return errors.New("summa musbat bo‘lishi kerak")
}
a.balance += amount
return nil
}
func (a Account) Balance() int64 {
return a.balance
}
func main() {
account := Account{}
if err := account.Deposit(50_000); err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println(account.Balance())
}
Natija:
balance kichik harf bilan boshlangan, shuning uchun boshqa paket undan bevosita foydalana olmaydi. Qiymatni o‘zgartirish Deposit() orqali, o‘qish esa Balance() orqali bajariladi. Shu tariqa tekshiruv bir joyda saqlanadi.
Metodni chaqirish usullari
Metod odatda qiymat orqali chaqiriladi:
Go metodni funksiya qiymati sifatida ham olish imkonini beradi. Method value receiverni o‘zida saqlaydi:
package main
import "fmt"
type Counter struct {
value int
}
func (c *Counter) Add(n int) {
c.value += n
}
func main() {
counter := &Counter{}
add := counter.Add
add(3)
add(2)
fmt.Println(counter.value)
}
Natija:
add o‘zgaruvchisi counter receiveriga bog‘langan. Shu sababli uni chaqirganda receiver qayta berilmaydi.
Method expression esa receiverni oddiy birinchi argumentga aylantiradi:
Bu ko‘rinish bir metodni turli receiverlar bilan funksiya sifatida uzatish kerak bo‘lganda foydali.
Avtomatik manzil olish va ochish
Quyidagi ikki chaqiruv ko‘p holatda bir xil ishlaydi:
account manzili olinadigan qiymat bo‘lgani uchun kompilyator pointer receiverli metod chaqirig‘ida &accountni avtomatik qo‘shadi. Pointerda value receiverli metod chaqirilsa, u ham avtomatik ochiladi:
Bu qulaylik metodni chaqirishga tegishli. U Account va *Accountning metodlar to‘plamini bir xil qilib qo‘ymaydi.
Receiver tanlash
Pointer receiver tanlang, agar:
- metod receiver holatini o‘zgartirsa;
- struct katta bo‘lsa va nusxalashni kamaytirish muhim bo‘lsa;
- turda
sync.Mutexkabi nusxalanmasligi kerak bo‘lgan maydon bo‘lsa; - boshqa metodlar allaqachon pointer receiver ishlatsa va izchillik kerak bo‘lsa.
Value receiver kichik, o‘zgarmas qiymatga o‘xshash turlarda qulay. Receiver ichida reference semantikaga ega map yoki slice bo‘lsa, value receiver ham uning asosiy ma’lumotini o‘zgartira olishi mumkin. Shuning uchun tanlovni faqat “metod receiver maydoniga assignment qiladimi” bilan cheklamang.
Method set va interface bilan bog‘lanish
T turining metodlar to‘plamiga T receiverli metodlar kiradi. *T metodlar to‘plamiga esa T va *T receiverli metodlar kiradi. Natijada faqat pointer receiver bilan e’lon qilingan metodni talab qiladigan interface’ni odatda *T bajaradi, T emas.
Bu farq metodni oddiy value.Method() ko‘rinishida chaqirishdagi avtomatik & bilan aralashmasligi kerak. Interface’ga moslik method set orqali kompilyatsiya vaqtida tekshiriladi.
Quyidagi misolda Increment() faqat pointer receiver bilan e’lon qilingan:
package main
import "fmt"
type Incrementer interface {
Increment()
}
type Counter int
func (c *Counter) Increment() {
*c++
}
func run(value Incrementer) {
value.Increment()
}
func main() {
var counter Counter
run(&counter)
fmt.Println(counter)
}
Natija:
run(counter) ishlamaydi, chunki Counterning metodlar to‘plamida pointer receiverli Increment() yo‘q. run(&counter) ishlaydi, chunki *Counter metodlar to‘plamiga bu metod kiradi.
Receiver nomini tanlash
Receiver nomi odatda tur nomining bir yoki ikki harfidan olinadi:
this yoki self kabi nomlar Go uslubida odatda ishlatilmaydi. Bir tur metodlarida receiver nomini izchil saqlash kodni o‘qishni osonlashtiradi.
Xulosa
- Metod receiver orqali nomlangan turga bog‘lanadi.
- Value receiver nusxa bilan ishlaydi.
- Pointer receiver asl qiymatni o‘zgartira oladi va
nilbo‘lishi mumkin. - Receiver tanlovi semantika, izchillik va nusxalash xarajatiga asoslanadi.
Tva*Tning method setlari interface’ga moslikda farq qiladi.- Method value receiverni saqlaydi, method expression esa receiverni argument sifatida qabul qiladi.
Keyingi darsda metodlar to‘plami orqali tur xatti-harakatini ifodalovchi interface ko‘rib chiqiladi.