Типове и интерфейси

17.10.2013

Но преди това...

Въпрос за мъфин #1

Какво са масивите в Go?
Могат ли да имат променлива дължина?
Какво е range?

Въпрос за мъфин #2

Как са имплементирани слайсовете в Go?

Въпрос за мъфин #3

arr := [6]float64{1,2,3,4,5,6}
x := arr[1:]
slice1 := append(x, 4)
Какви типове имат arr, x, slice1?
Какво ще върнат len(x), cap(x), len(slice1), cap(slice1)?

Въпрос за мъфин #4

Какво прави този код:

var x map[string]int
x["key"] = 10
if name, ok := x["key"]; ok {
    fmt.Println(name, ok)
}

Въпрос за мъфин #5

Каква е разликата между new и make и кога се ползва едното, и кога другото?

Собствени типове

type integer int
type float float64
type chars string

Нека разгледаме функцията Abs

func Abs(i integer) integer {
    switch {
        case i < 0:
            return -i
        case i == 0:
            return 0
        default:
            return i
    }
}

var number integer = -42
positiveInteger := Abs(number)

Обектно-ориентираният начин да се направи подобно нещо

func (i integer) Abs() integer {
    switch {
        case i < 0:
            return -i
        case i == 0:
            return 0
        default:
            return i
    }
}

var number integer = -42
number.Abs()

Какво точно е метод?

Що е то receiver-а?

* По стойност
- Работи се върху копие на обекта
- Това може да е скъпа операция за големи обекти

* Като указател
- Работи се върху самия обект
- Всяка промяна в метода се отразява на оригиналния обект

Пример

package main

import "fmt"

type integer int

func (i integer) Abs() integer {
    switch {
    case i < 0:
        return -i
    case i == 0:
        return 0
    default:
        return i
    }
}

func (i *integer) Increment() {
    *i++
}

func main() {
    var number integer
    number = -42

    number.Increment()
    fmt.Println(number.Abs())
}

struct

type Rectangle struct {
    x, y int
}

type Triangle struct {
    x, y, z int
}

Методи за тези типове

func (r *Rectangle) Circumference() int {
    return 2 * (r.x + r.y)
}

func (r *Triangle) Circumference() int {
    return r.x + r.y + r.z
}

Интерфейси

Дефиниция на интерфейс

type Shape interface {
    Circumference()
}

Пример

package main

import "fmt"

type Shape interface {
	Circumference() int
}

type Rectangle struct {
	x, y int
}

func (r *Rectangle) Circumference() int {
	return r.x + r.y
}

type Triangle struct {
	x, y, z int
}

func (t *Triangle) Circumference() int {
	return t.x + t.y + t.z
}

func sumOfCircumferences(shapes ...Shape) int {
    sum := 0
    for _, shape := range shapes {
        sum += shape.Circumference()
    }
    return sum
}

func main() {
	rect := &Rectangle{x: 12, y: 64}
	tr := &Triangle{x: 12, y: 64, z: 50}
	fmt.Println(sumOfCircumferences(rect, tr))
}

Вложени типове

Композиция

Конструираме един тип, комбинирайки няколко прости други типa.

* Пример:
Искаме да си направим smartphone. Не откриваме топлата вода, а просто го наблъскваме с каквито джаджи се сетим.

type Smartphone struct {
    phone BasicPhone
    camera CameraModule
    wifi WiFiModule
    screen MultiTouchScreen
    battery DamnHugeBattery
}

Всеки един от тези типове отговаря за точно едно нещо и може да бъде използвано самостоятелно.

Квази-Наследяване

Вярваме, че знаете как работи то. Дори сме сигурни, че сте правили хора и студенти:

type Student struct {
    Person
    facultyNumber int16
}

Вложеният тип, е анонимен, което присвоява всичките му методи и атрибути на базовия клас.

Множествено наследяване

Да, имате право на много анонимни вложени типа. Не го правете.

Stringer

type Stringer interface {
    String() string
}

Всеки тип, който имплементира този интерфейс, може да бъде принтиран в Printf например с %s.
Printf просто ще извиква String() и ще вземе стойността.

Duck typing

Всеки обект имплементира празния интерфейс

interface{}

С променлива от такъв тип не можем да правим абсолютно нищо. Това може да звучи безполезно, но не е, ако имаме следното...

Type Assertions

var value interface{}
value = 20
value = "asd"
str := value.(string)

На последния ред или ще се паникьосаме, или в str ще имаме стойността на value, ако тя наистина е била от тип string.

Interface Conversions

var value interface{}
switch str := value.(type) {
case string:
    return str
case Stringer:
    return str.String()

Начин да се държим по различен начин въз основа на типа на нещо.

Въпроси?